Bug 697230: Part 1 - Centralize style image observers. r=bz

This commit is contained in:
Kyle Huey 2012-08-13 15:04:19 -07:00
parent 0a624c1ae1
commit 2e07e6f8c2
17 changed files with 737 additions and 559 deletions

View File

@ -69,6 +69,7 @@ class nsWindowSizes;
namespace mozilla {
namespace css {
class Loader;
class ImageLoader;
} // namespace css
namespace dom {
@ -78,8 +79,8 @@ class Element;
} // namespace mozilla
#define NS_IDOCUMENT_IID \
{ 0xbd70ee06, 0x2a7d, 0x4258, \
{ 0x86, 0x4b, 0xbd, 0x28, 0xad, 0x9f, 0xd1, 0x41 } }
{ 0xdb888523, 0x541f, 0x49e3, \
{ 0xa9, 0x71, 0xb5, 0xea, 0xd1, 0xf0, 0xc3, 0xcf } }
// Flag for AddStyleSheet().
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
@ -585,6 +586,13 @@ public:
return mCSSLoader;
}
/**
* Get this document's StyleImageLoader. This is guaranteed to not return null.
*/
mozilla::css::ImageLoader* StyleImageLoader() const {
return mStyleImageLoader;
}
/**
* Get the channel that was passed to StartDocumentLoad or Reset for this
* document. Note that this may be null in some cases (eg if
@ -1739,6 +1747,7 @@ protected:
// We hold a strong reference to mNodeInfoManager through mNodeInfo
nsNodeInfoManager* mNodeInfoManager; // [STRONG]
nsRefPtr<mozilla::css::Loader> mCSSLoader;
mozilla::css::ImageLoader* mStyleImageLoader; // [STRONG]
nsHTMLStyleSheet* mAttrStyleSheet;
// The set of all object, embed, applet, video and audio elements for

View File

@ -26,6 +26,7 @@
#include "nsIObserver.h"
#include "nsIBaseWindow.h"
#include "mozilla/css/Loader.h"
#include "mozilla/css/ImageLoader.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIScriptRuntime.h"
@ -1646,6 +1647,11 @@ nsDocument::~nsDocument()
mCSSLoader->DropDocumentReference();
}
if (mStyleImageLoader) {
mStyleImageLoader->DropDocumentReference();
NS_RELEASE(mStyleImageLoader);
}
delete mHeaderData;
if (mBoxObjectTable) {
@ -1976,7 +1982,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
nsresult
nsDocument::Init()
{
if (mCSSLoader || mNodeInfoManager || mScriptLoader) {
if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
return NS_ERROR_ALREADY_INITIALIZED;
}
@ -2000,8 +2006,11 @@ nsDocument::Init()
// Assume we're not quirky, until we know otherwise
mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
mStyleImageLoader = new mozilla::css::ImageLoader(this);
NS_ADDREF(mStyleImageLoader);
mNodeInfoManager = new nsNodeInfoManager();
nsresult rv = mNodeInfoManager->Init(this);
nsresult rv = mNodeInfoManager->Init(this);
NS_ENSURE_SUCCESS(rv, rv);
// mNodeInfo keeps NodeInfoManager alive!

View File

@ -78,7 +78,6 @@ CPPSRCS = \
nsFrameManager.cpp \
nsFrameIterator.cpp \
nsGenConList.cpp \
nsImageLoader.cpp \
nsLayoutDebugger.cpp \
nsLayoutHistoryState.cpp \
nsLayoutUtils.cpp \

View File

@ -45,8 +45,10 @@
#include "gfxDrawable.h"
#include "sampler.h"
#include "nsCSSRenderingBorders.h"
#include "mozilla/css/ImageLoader.h"
using namespace mozilla;
using namespace mozilla::css;
// To avoid storing this data on nsInlineFrame (bloat) and to avoid
// recalculating this for each frame in a continuation (perf), hold
@ -2271,7 +2273,17 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
// Ensure we get invalidated for loads of the image. We need to do
// this here because this might be the only code that knows about the
// association of the style data with the frame.
aPresContext->SetupBackgroundImageLoaders(aForFrame, bg);
if (aBackgroundSC != aForFrame->GetStyleContext()) {
ImageLoader* loader = aPresContext->Document()->StyleImageLoader();
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
if (bg->mLayers[i].mImage.GetType() == eStyleImageType_Image) {
imgIRequest *image = bg->mLayers[i].mImage.GetImageData();
loader->AssociateRequestToFrame(image, aForFrame);
}
}
}
// The background color is rendered over the entire dirty area,
// even if the image isn't.
@ -2574,9 +2586,11 @@ DrawBorderImage(nsPresContext* aPresContext,
// XXX We shouldn't really... since if anybody is passing in a
// different style, they'll potentially have the wrong size for the
// border too.
aPresContext->SetupBorderImageLoaders(aForFrame, &aStyleBorder);
imgIRequest *req = aStyleBorder.GetBorderImage();
ImageLoader* loader = aPresContext->Document()->StyleImageLoader();
// If this fails there's not much we can do ...
loader->AssociateRequestToFrame(req, aForFrame);
// Get the actual image.

View File

@ -1,250 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* class to notify frames of background image loads */
#include "nsImageLoader.h"
#include "imgILoader.h"
#include "nsIURI.h"
#include "nsILoadGroup.h"
#include "nsNetUtil.h"
#include "nsPresContext.h"
#include "nsIPresShell.h"
#include "nsIFrame.h"
#include "nsIContent.h"
#include "imgIContainer.h"
#include "nsStyleContext.h"
#include "nsGkAtoms.h"
#include "nsLayoutUtils.h"
// Paint forcing
#include "prenv.h"
NS_IMPL_ISUPPORTS2(nsImageLoader, imgIDecoderObserver, imgIContainerObserver)
nsImageLoader::nsImageLoader(nsIFrame *aFrame, PRUint32 aActions,
nsImageLoader *aNextLoader)
: mFrame(aFrame),
mActions(aActions),
mNextLoader(aNextLoader),
mRequestRegistered(false)
{
}
nsImageLoader::~nsImageLoader()
{
Destroy();
}
/* static */ already_AddRefed<nsImageLoader>
nsImageLoader::Create(nsIFrame *aFrame, imgIRequest *aRequest,
PRUint32 aActions, nsImageLoader *aNextLoader)
{
nsRefPtr<nsImageLoader> loader =
new nsImageLoader(aFrame, aActions, aNextLoader);
loader->Load(aRequest);
return loader.forget();
}
void
nsImageLoader::Destroy()
{
// Destroy the chain with only one level of recursion.
nsRefPtr<nsImageLoader> list = mNextLoader;
mNextLoader = nullptr;
while (list) {
nsRefPtr<nsImageLoader> todestroy = list;
list = todestroy->mNextLoader;
todestroy->mNextLoader = nullptr;
todestroy->Destroy();
}
if (mRequest && mFrame) {
nsLayoutUtils::DeregisterImageRequest(mFrame->PresContext(), mRequest,
&mRequestRegistered);
}
mFrame = nullptr;
if (mRequest) {
mRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
}
mRequest = nullptr;
}
nsresult
nsImageLoader::Load(imgIRequest *aImage)
{
NS_ASSERTION(!mRequest, "can't reuse image loaders");
NS_ASSERTION(mFrame, "not initialized");
NS_ASSERTION(aImage, "must have non-null image");
if (!mFrame)
return NS_ERROR_NOT_INITIALIZED;
if (!aImage)
return NS_ERROR_FAILURE;
// Deregister mRequest from the refresh driver, since it is no longer
// going to be managed by this nsImageLoader.
nsPresContext* presContext = mFrame->PresContext();
nsLayoutUtils::DeregisterImageRequest(presContext, mRequest,
&mRequestRegistered);
// Make sure to clone into a temporary, then set mRequest, since
// cloning may notify and we don't want to trigger paints from this
// code.
nsCOMPtr<imgIRequest> newRequest;
nsresult rv = aImage->Clone(this, getter_AddRefs(newRequest));
mRequest.swap(newRequest);
if (mRequest) {
nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mRequest,
&mRequestRegistered);
}
return rv;
}
NS_IMETHODIMP nsImageLoader::OnStartContainer(imgIRequest *aRequest,
imgIContainer *aImage)
{
NS_ABORT_IF_FALSE(aImage, "Who's calling us then?");
/* Get requested animation policy from the pres context:
* normal = 0
* one frame = 1
* one loop = 2
*/
aImage->SetAnimationMode(mFrame->PresContext()->ImageAnimationMode());
return NS_OK;
}
NS_IMETHODIMP nsImageLoader::OnStopFrame(imgIRequest *aRequest,
PRUint32 aFrame)
{
if (!mFrame)
return NS_ERROR_FAILURE;
if (!mRequest) {
// We're in the middle of a paint anyway
return NS_OK;
}
// Take requested actions
if (mActions & ACTION_REDRAW_ON_DECODE) {
DoRedraw(nullptr);
}
return NS_OK;
}
NS_IMETHODIMP nsImageLoader::OnImageIsAnimated(imgIRequest *aRequest)
{
// Register with the refresh driver now that we are aware that
// we are animated.
nsLayoutUtils::RegisterImageRequest(mFrame->PresContext(),
aRequest, &mRequestRegistered);
return NS_OK;
}
NS_IMETHODIMP nsImageLoader::OnStopRequest(imgIRequest *aRequest,
bool aLastPart)
{
if (!mFrame)
return NS_ERROR_FAILURE;
if (!mRequest) {
// We're in the middle of a paint anyway
return NS_OK;
}
// Take requested actions
if (mActions & ACTION_REDRAW_ON_LOAD) {
DoRedraw(nullptr);
}
return NS_OK;
}
NS_IMETHODIMP nsImageLoader::FrameChanged(imgIRequest *aRequest,
imgIContainer *aContainer,
const nsIntRect *aDirtyRect)
{
if (!mFrame)
return NS_ERROR_FAILURE;
if (!mRequest) {
// We're in the middle of a paint anyway
return NS_OK;
}
NS_ASSERTION(aRequest == mRequest, "This is a neat trick.");
nsRect r = aDirtyRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect()) ?
nsRect(nsPoint(0, 0), mFrame->GetSize()) :
aDirtyRect->ToAppUnits(nsPresContext::AppUnitsPerCSSPixel());
DoRedraw(&r);
return NS_OK;
}
void
nsImageLoader::DoRedraw(const nsRect* aDamageRect)
{
// 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...
nsRect bounds(nsPoint(0, 0), mFrame->GetSize());
if (mFrame->GetType() == nsGkAtoms::canvasFrame) {
// The canvas's background covers the whole viewport.
bounds = mFrame->GetVisualOverflowRect();
}
// 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.
nsStyleContext* styleContext;
mFrame->GetStyleContext(&styleContext);
const nsStyleBackground* bg = styleContext->GetStyleBackground();
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 (mFrame->GetStyleVisibility()->IsVisible()) {
mFrame->Invalidate(bounds);
}
}

View File

@ -1,82 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* class to notify frames of background and border image loads */
#include "nsStubImageDecoderObserver.h"
class nsIFrame;
class nsIURI;
#include "imgIRequest.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
/**
* Image loaders pass notifications for background and border image
* loading and animation on to the frames.
*
* Each frame's image loaders form a linked list.
*/
class nsImageLoader : public nsStubImageDecoderObserver
{
private:
nsImageLoader(nsIFrame *aFrame, PRUint32 aActions,
nsImageLoader *aNextLoader);
virtual ~nsImageLoader();
public:
/*
* Flags to specify actions that can be taken for the image at various
* times. Reflows always occur before redraws. "Decode" refers to one
* frame being available, whereas "load" refers to all the data being loaded
* from the network.
*/
enum {
ACTION_REDRAW_ON_DECODE = 0x01,
ACTION_REDRAW_ON_LOAD = 0x02,
};
static already_AddRefed<nsImageLoader>
Create(nsIFrame *aFrame, imgIRequest *aRequest,
PRUint32 aActions, nsImageLoader *aNextLoader);
NS_DECL_ISUPPORTS
// imgIDecoderObserver (override nsStubImageDecoderObserver)
NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
NS_IMETHOD OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame);
NS_IMETHOD OnStopRequest(imgIRequest *aRequest, bool aLastPart);
NS_IMETHOD OnImageIsAnimated(imgIRequest *aRequest);
// Do not override OnDataAvailable since background images are not
// displayed incrementally; they are displayed after the entire image
// has been loaded.
// Note: Images referenced by the <img> element are displayed
// incrementally in nsImageFrame.cpp.
// imgIContainerObserver (override nsStubImageDecoderObserver)
NS_IMETHOD FrameChanged(imgIRequest *aRequest,
imgIContainer *aContainer,
const nsIntRect *aDirtyRect);
void Destroy();
imgIRequest *GetRequest() { return mRequest; }
nsImageLoader *GetNextLoader() { return mNextLoader; }
private:
nsresult Load(imgIRequest *aImage);
/* if aDamageRect is nullptr, the whole frame is redrawn. */
void DoRedraw(const nsRect* aDamageRect);
nsIFrame *mFrame;
nsCOMPtr<imgIRequest> mRequest;
PRUint32 mActions;
nsRefPtr<nsImageLoader> mNextLoader;
// This is a boolean flag indicating whether or not the current image request
// has been registered with the refresh driver.
bool mRequestRegistered;
};

View File

@ -14,7 +14,6 @@
#include "nsIContentViewer.h"
#include "nsPIDOMWindow.h"
#include "nsStyleSet.h"
#include "nsImageLoader.h"
#include "nsIContent.h"
#include "nsIFrame.h"
#include "nsIURL.h"
@ -63,6 +62,7 @@
#include "FrameLayerBuilder.h"
#include "nsDOMMediaQueryList.h"
#include "nsSMILAnimationController.h"
#include "mozilla/css/ImageLoader.h"
#ifdef IBMBIDI
#include "nsBidiPresUtils.h"
@ -162,14 +162,6 @@ IsVisualCharset(const nsCString& aCharset)
}
#endif // IBMBIDI
static PLDHashOperator
destroy_loads(nsIFrame* aKey, nsRefPtr<nsImageLoader>& aData, void* closure)
{
aData->Destroy();
return PL_DHASH_NEXT;
}
#include "nsContentCID.h"
// NOTE! nsPresContext::operator new() zeroes out all members, so don't
@ -307,28 +299,12 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPresContext)
static PLDHashOperator
TraverseImageLoader(nsIFrame* aKey, nsRefPtr<nsImageLoader>& aData,
void* aClosure)
{
nsCycleCollectionTraversalCallback *cb =
static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mImageLoaders[i] item");
cb->NoteXPCOMChild(aData);
return PL_DHASH_NEXT;
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mEventManager, nsIObserver);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
tmp->mImageLoaders[i].Enumerate(TraverseImageLoader, &cb);
// We own only the items in mDOMMediaQueryLists that have listeners;
// this reference is managed by their AddListener and RemoveListener
// methods.
@ -915,10 +891,6 @@ nsPresContext::Init(nsDeviceContext* aDeviceContext)
mDeviceContext->FlushFontCache();
mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel();
for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) {
mImageLoaders[i].Init();
}
mEventManager = new nsEventStateManager();
mTransitionManager = new nsTransitionManager(this);
@ -1091,18 +1063,6 @@ nsPresContext::SetShell(nsIPresShell* aShell)
}
}
void
nsPresContext::DestroyImageLoaders()
{
// Destroy image loaders. This is important to do when frames are being
// destroyed because imageloaders can have pointers to frames and we don't
// want those pointers to outlive the destruction of the frame arena.
for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) {
mImageLoaders[i].Enumerate(destroy_loads, nullptr);
mImageLoaders[i].Clear();
}
}
void
nsPresContext::DoChangeCharSet(const nsCString& aCharSet)
{
@ -1252,18 +1212,6 @@ static void SetImgAnimModeOnImgReq(imgIRequest* aImgReq, PRUint16 aMode)
}
}
// Enumeration call back for HashTable
static PLDHashOperator
set_animation_mode(nsIFrame* aKey, nsRefPtr<nsImageLoader>& aData, void* closure)
{
for (nsImageLoader *loader = aData; loader;
loader = loader->GetNextLoader()) {
imgIRequest* imgReq = loader->GetRequest();
SetImgAnimModeOnImgReq(imgReq, (PRUint16)NS_PTR_TO_INT32(closure));
}
return PL_DHASH_NEXT;
}
// IMPORTANT: Assumption is that all images for a Presentation
// have the same Animation Mode (pavlov said this was OK)
//
@ -1318,15 +1266,13 @@ nsPresContext::SetImageAnimationModeInternal(PRUint16 aMode)
if (!IsDynamic())
return;
// Set the mode on the image loaders.
for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
mImageLoaders[i].Enumerate(set_animation_mode, NS_INT32_TO_PTR(aMode));
// Now walk the content tree and set the animation mode
// on all the images.
if (mShell != nullptr) {
nsIDocument *doc = mShell->GetDocument();
if (doc) {
doc->StyleImageLoader()->SetAnimationMode(aMode);
Element *rootElement = doc->GetRootElement();
if (rootElement) {
SetImgAnimations(rootElement, aMode);
@ -1437,68 +1383,6 @@ nsPresContext::ScreenWidthInchesForFontInflation(bool* aChanged)
return deviceWidthInches;
}
void
nsPresContext::SetImageLoaders(nsIFrame* aTargetFrame,
ImageLoadType aType,
nsImageLoader* aImageLoaders)
{
NS_ASSERTION(mShell || !aImageLoaders,
"Shouldn't add new image loader after the shell is gone");
nsRefPtr<nsImageLoader> oldLoaders;
mImageLoaders[aType].Get(aTargetFrame, getter_AddRefs(oldLoaders));
if (aImageLoaders) {
mImageLoaders[aType].Put(aTargetFrame, aImageLoaders);
} else if (oldLoaders) {
mImageLoaders[aType].Remove(aTargetFrame);
}
if (oldLoaders)
oldLoaders->Destroy();
}
void
nsPresContext::SetupBackgroundImageLoaders(nsIFrame* aFrame,
const nsStyleBackground* aStyleBackground)
{
nsRefPtr<nsImageLoader> loaders;
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, aStyleBackground) {
if (aStyleBackground->mLayers[i].mImage.GetType() == eStyleImageType_Image) {
PRUint32 actions = nsImageLoader::ACTION_REDRAW_ON_DECODE;
imgIRequest *image = aStyleBackground->mLayers[i].mImage.GetImageData();
loaders = nsImageLoader::Create(aFrame, image, actions, loaders);
}
}
SetImageLoaders(aFrame, BACKGROUND_IMAGE, loaders);
}
void
nsPresContext::SetupBorderImageLoaders(nsIFrame* aFrame,
const nsStyleBorder* aStyleBorder)
{
// We get called the first time we try to draw a border-image, and
// also when the border image changes (including when it changes from
// non-null to null).
imgIRequest *borderImage = aStyleBorder->GetBorderImage();
if (!borderImage) {
SetImageLoaders(aFrame, BORDER_IMAGE, nullptr);
return;
}
PRUint32 actions = nsImageLoader::ACTION_REDRAW_ON_LOAD;
nsRefPtr<nsImageLoader> loader =
nsImageLoader::Create(aFrame, borderImage, actions, nullptr);
SetImageLoaders(aFrame, BORDER_IMAGE, loader);
}
void
nsPresContext::StopImagesFor(nsIFrame* aTargetFrame)
{
for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
SetImageLoaders(aTargetFrame, ImageLoadType(i), nullptr);
}
void
nsPresContext::SetContainer(nsISupports* aHandler)
{
@ -2226,6 +2110,8 @@ NotifyDidPaintSubdocumentCallback(nsIDocument* aDocument, void* aData)
void
nsPresContext::NotifyDidPaintForSubtree()
{
Document()->StyleImageLoader()->NotifyPaint();
if (!mFireAfterPaintEvents)
return;
mFireAfterPaintEvents = false;

View File

@ -32,7 +32,6 @@
#include "mozilla/TimeStamp.h"
#include "prclist.h"
class nsImageLoader;
#ifdef IBMBIDI
class nsBidiPresUtils;
#endif // IBMBIDI
@ -360,49 +359,6 @@ public:
bool GetFocusRingOnAnything() const { return mFocusRingOnAnything; }
PRUint8 GetFocusRingStyle() const { return mFocusRingStyle; }
/**
* The types of image load types that the pres context needs image
* loaders to track invalidation for.
*/
enum ImageLoadType {
BACKGROUND_IMAGE,
BORDER_IMAGE,
IMAGE_LOAD_TYPE_COUNT
};
/**
* Set the list of image loaders that track invalidation for a
* specific frame and type of image. This list will replace any
* previous list for that frame and image type (and null will remove
* any previous list).
*/
NS_HIDDEN_(void) SetImageLoaders(nsIFrame* aTargetFrame,
ImageLoadType aType,
nsImageLoader* aImageLoaders);
/**
* Make an appropriate SetImageLoaders call (including potentially
* with null aImageLoaders) given that aFrame draws its background
* based on aStyleBackground.
*/
NS_HIDDEN_(void) SetupBackgroundImageLoaders(nsIFrame* aFrame,
const nsStyleBackground*
aStyleBackground);
/**
* Make an appropriate SetImageLoaders call (including potentially
* with null aImageLoaders) given that aFrame draws its border
* based on aStyleBorder.
*/
NS_HIDDEN_(void) SetupBorderImageLoaders(nsIFrame* aFrame,
const nsStyleBorder* aStyleBorder);
/**
* This method is called when a frame is being destroyed to
* ensure that the image loads get disassociated from the prescontext
*/
NS_HIDDEN_(void) StopImagesFor(nsIFrame* aTargetFrame);
NS_HIDDEN_(void) SetContainer(nsISupports* aContainer);
virtual NS_HIDDEN_(already_AddRefed<nsISupports>) GetContainerExternal() const;
@ -933,8 +889,6 @@ public:
}
inline void ForgetUpdatePluginGeometryFrame(nsIFrame* aFrame);
void DestroyImageLoaders();
bool GetContainsUpdatePluginGeometryFrame()
{
return mContainsUpdatePluginGeometryFrame;
@ -1106,9 +1060,6 @@ public:
protected:
nsRefPtrHashtable<nsPtrHashKey<nsIFrame>, nsImageLoader>
mImageLoaders[IMAGE_LOAD_TYPE_COUNT];
nsWeakPtr mContainer;
PRCList mDOMMediaQueryLists;

View File

@ -172,6 +172,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "sampler.h"
#include "mozilla/css/ImageLoader.h"
#include "Layers.h"
#include "nsAsyncDOMEvent.h"
@ -1956,11 +1957,10 @@ PresShell::FireResizeEvent()
void
PresShell::SetIgnoreFrameDestruction(bool aIgnore)
{
if (mPresContext) {
// We need to destroy the image loaders first, as they won't be
// notified when frames are destroyed once this setting takes effect.
// (See bug 673984)
mPresContext->DestroyImageLoaders();
if (mDocument) {
// We need to tell the ImageLoader to drop all its references to frames
// because they're about to go away and it won't get notifications of that.
mDocument->StyleImageLoader()->ClearAll();
}
mIgnoreFrameDestruction = aIgnore;
}
@ -1973,7 +1973,7 @@ PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
mPresContext->ForgetUpdatePluginGeometryFrame(aFrame);
if (!mIgnoreFrameDestruction) {
mPresContext->StopImagesFor(aFrame);
mDocument->StyleImageLoader()->DropRequestsForFrame(aFrame);
mFrameConstructor->NotifyDestroyingFrame(aFrame);

View File

@ -97,10 +97,12 @@
#include "mozilla/Preferences.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/css/ImageLoader.h"
using namespace mozilla;
using namespace mozilla::layers;
using namespace mozilla::layout;
using namespace mozilla::css;
// Struct containing cached metrics for box-wrapped frames.
struct nsBoxLayoutMetrics
@ -698,27 +700,50 @@ EqualImages(imgIRequest *aOldImage, imgIRequest *aNewImage)
/* virtual */ void
nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
{
if (aOldStyleContext) {
// If the old context had a background image image and new context
// does not have the same image, clear the image load notifier
// (which keeps the image loading, if it still is) for the frame.
// We want to do this conservatively because some frames paint their
// backgrounds from some other frame's style data, and we don't want
// to clear those notifiers unless we have to. (They'll be reset
// when we paint, although we could miss a notification in that
// interval.)
const nsStyleBackground *oldBG = aOldStyleContext->GetStyleBackground();
const nsStyleBackground *newBG = GetStyleBackground();
ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader();
// If the old context had a background image image and new context
// does not have the same image, clear the image load notifier
// (which keeps the image loading, if it still is) for the frame.
// We want to do this conservatively because some frames paint their
// backgrounds from some other frame's style data, and we don't want
// to clear those notifiers unless we have to. (They'll be reset
// when we paint, although we could miss a notification in that
// interval.)
const nsStyleBackground *oldBG = aOldStyleContext ?
aOldStyleContext->GetStyleBackground() :
nullptr;
const nsStyleBackground *newBG = GetStyleBackground();
if (oldBG) {
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, oldBG) {
// If there is an image in oldBG that's not in newBG, drop it.
if (i >= newBG->mImageCount ||
oldBG->mLayers[i].mImage != newBG->mLayers[i].mImage) {
// stop the image loading for the frame, the image has changed
PresContext()->SetImageLoaders(this,
nsPresContext::BACKGROUND_IMAGE, nullptr);
break;
}
}
const nsStyleImage& oldImage = oldBG->mLayers[i].mImage;
if (oldImage.GetType() != eStyleImageType_Image) {
continue;
}
imageLoader->DisassociateRequestFromFrame(oldImage.GetImageData(),
this);
}
}
}
NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, newBG) {
// If there is an image in newBG that's not in oldBG, add it.
if (!oldBG || i >= oldBG->mImageCount ||
newBG->mLayers[i].mImage != oldBG->mLayers[i].mImage) {
const nsStyleImage& newImage = newBG->mLayers[i].mImage;
if (newImage.GetType() != eStyleImageType_Image) {
continue;
}
imageLoader->AssociateRequestToFrame(newImage.GetImageData(), this);
}
}
if (aOldStyleContext) {
// If we detect a change on margin, padding or border, we store the old
// values on the frame itself between now and reflow, so if someone
// calls GetUsed(Margin|Border|Padding)() before the next reflow, we
@ -757,6 +782,7 @@ nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
imgIRequest *oldBorderImage = aOldStyleContext
? aOldStyleContext->GetStyleBorder()->GetBorderImage()
: nullptr;
imgIRequest *newBorderImage = GetStyleBorder()->GetBorderImage();
// FIXME (Bug 759996): The following is no longer true.
// For border-images, we can't be as conservative (we need to set the
// new loaders if there has been any change) since the CalcDifference
@ -771,9 +797,14 @@ nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
// is loaded) and paint. We also don't really care about any callers
// who try to paint borders with a different style context, because
// they won't have the correct size for the border either.
if (!EqualImages(oldBorderImage, GetStyleBorder()->GetBorderImage())) {
if (!EqualImages(oldBorderImage, newBorderImage)) {
// stop and restart the image loading/notification
PresContext()->SetupBorderImageLoaders(this, GetStyleBorder());
if (oldBorderImage) {
imageLoader->DisassociateRequestFromFrame(oldBorderImage, this);
}
if (newBorderImage) {
imageLoader->AssociateRequestToFrame(newBorderImage, this);
}
}
// If the page contains markup that overrides text direction, and

View File

@ -0,0 +1,427 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* A class that handles style system image loads (other image loads are handled
* by the nodes in the content tree).
*/
#include "mozilla/css/ImageLoader.h"
#include "nsContentUtils.h"
#include "nsLayoutUtils.h"
#include "nsNetError.h"
namespace mozilla {
namespace css {
/* static */ PLDHashOperator
ImageLoader::SetAnimationModeEnumerator(nsISupports* aKey, FrameSet* aValue,
void* aClosure)
{
imgIRequest* request = static_cast<imgIRequest*>(aKey);
PRUint16* mode = static_cast<PRUint16*>(aClosure);
#ifdef DEBUG
{
nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(aKey);
NS_ASSERTION(debugRequest == request, "This is bad");
}
#endif
nsCOMPtr<imgIContainer> container;
request->GetImage(getter_AddRefs(container));
if (!container) {
return PL_DHASH_NEXT;
}
// This can fail if the image is in error, and we don't care.
container->SetAnimationMode(*mode);
return PL_DHASH_NEXT;
}
void
ImageLoader::DropDocumentReference()
{
ClearAll();
mDocument = nsnull;
}
void
ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
nsIFrame* aFrame)
{
MOZ_ASSERT(mRequestToFrameMap.IsInitialized() &&
mFrameToRequestMap.IsInitialized() &&
mImages.IsInitialized());
nsCOMPtr<imgIDecoderObserver> observer;
aRequest->GetDecoderObserver(getter_AddRefs(observer));
if (!observer) {
// The request has already been canceled, so ignore it. This is ok because
// we're not going to get any more notifications from a canceled request.
return;
}
MOZ_ASSERT(observer == this);
FrameSet* frameSet = nsnull;
if (mRequestToFrameMap.Get(aRequest, &frameSet)) {
NS_ASSERTION(frameSet, "This should never be null!");
}
if (!frameSet) {
nsAutoPtr<FrameSet> newFrameSet(new FrameSet());
mRequestToFrameMap.Put(aRequest, newFrameSet);
frameSet = newFrameSet.forget();
}
RequestSet* requestSet = nsnull;
if (mFrameToRequestMap.Get(aFrame, &requestSet)) {
NS_ASSERTION(requestSet, "This should never be null");
}
if (!requestSet) {
nsAutoPtr<RequestSet> newRequestSet(new RequestSet());
mFrameToRequestMap.Put(aFrame, newRequestSet);
requestSet = newRequestSet.forget();
}
// Add these to the sets, but only if they're not already there.
PRUint32 i;
if (!frameSet->GreatestIndexLtEq(aFrame, i)) {
frameSet->InsertElementAt(i, aFrame);
}
if (!requestSet->GreatestIndexLtEq(aRequest, i)) {
requestSet->InsertElementAt(i, aRequest);
}
}
void
ImageLoader::MaybeRegisterCSSImage(nsCSSValue::Image* aImage)
{
NS_ASSERTION(aImage, "This should never be null!");
bool found = false;
aImage->mRequests.GetWeak(mDocument, &found);
if (found) {
// This document already has a request.
return;
}
imgIRequest* canonicalRequest = aImage->mRequests.GetWeak(nsnull);
if (!canonicalRequest) {
// The image was blocked or something.
return;
}
nsCOMPtr<imgIRequest> request;
// Ignore errors here. If cloning fails for some reason we'll put a null
// entry in the hash and we won't keep trying to clone.
mInClone = true;
canonicalRequest->Clone(this, getter_AddRefs(request));
mInClone = false;
aImage->mRequests.Put(mDocument, request);
AddImage(aImage);
}
void
ImageLoader::DeregisterCSSImage(nsCSSValue::Image* aImage)
{
RemoveImage(aImage);
}
void
ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest,
nsIFrame* aFrame)
{
FrameSet* frameSet = nsnull;
RequestSet* requestSet = nsnull;
MOZ_ASSERT(mRequestToFrameMap.IsInitialized() &&
mFrameToRequestMap.IsInitialized() &&
mImages.IsInitialized());
#ifdef DEBUG
{
nsCOMPtr<imgIDecoderObserver> observer;
aRequest->GetDecoderObserver(getter_AddRefs(observer));
MOZ_ASSERT(!observer || observer == this);
}
#endif
mRequestToFrameMap.Get(aRequest, &frameSet);
mFrameToRequestMap.Get(aFrame, &requestSet);
if (frameSet) {
frameSet->RemoveElementSorted(aFrame);
}
if (requestSet) {
requestSet->RemoveElementSorted(aRequest);
}
if (frameSet && !frameSet->Length()) {
mRequestToFrameMap.Remove(aRequest);
nsPresContext* presContext = GetPresContext();
if (presContext) {
nsLayoutUtils::DeregisterImageRequest(presContext,
aRequest,
nsnull);
}
}
if (requestSet && !requestSet->Length()) {
mFrameToRequestMap.Remove(aFrame);
}
}
void
ImageLoader::DropRequestsForFrame(nsIFrame* aFrame)
{
RequestSet* requestSet = nsnull;
if (!mFrameToRequestMap.Get(aFrame, &requestSet)) {
return;
}
NS_ASSERTION(requestSet, "This should never be null");
RequestSet frozenRequestSet(*requestSet);
for (RequestSet::size_type i = frozenRequestSet.Length(); i != 0; --i) {
imgIRequest* request = frozenRequestSet.ElementAt(i - 1);
DisassociateRequestFromFrame(request, aFrame);
}
}
void
ImageLoader::SetAnimationMode(PRUint16 aMode)
{
NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
aMode == imgIContainer::kDontAnimMode ||
aMode == imgIContainer::kLoopOnceAnimMode,
"Wrong Animation Mode is being set!");
mRequestToFrameMap.EnumerateRead(SetAnimationModeEnumerator, &aMode);
}
static PLDHashOperator
ClearImageHashSet(nsPtrHashKey<nsCSSValue::Image>* aKey, void* aClosure)
{
nsIDocument* doc = static_cast<nsIDocument*>(aClosure);
nsCSSValue::Image* image = aKey->GetKey();
imgIRequest* request = image->mRequests.GetWeak(doc);
if (request) {
request->CancelAndForgetObserver(NS_BINDING_ABORTED);
}
image->mRequests.Remove(doc);
return PL_DHASH_REMOVE;
}
void
ImageLoader::ClearAll()
{
mRequestToFrameMap.Clear();
mFrameToRequestMap.Clear();
mImages.EnumerateEntries(&ClearImageHashSet, mDocument);
}
void
ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
nsIURI* aReferrer, nsCSSValue::Image* aImage)
{
NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?");
aImage->mRequests.Put(nsnull, nsnull);
if (!aURI) {
return;
}
if (!nsContentUtils::CanLoadImage(aURI, mDocument, mDocument,
aOriginPrincipal)) {
return;
}
nsCOMPtr<imgIRequest> request;
nsContentUtils::LoadImage(aURI, mDocument, aOriginPrincipal, aReferrer,
nsnull, nsIRequest::LOAD_NORMAL,
getter_AddRefs(request));
if (!request) {
return;
}
nsCOMPtr<imgIRequest> clonedRequest;
mInClone = true;
nsresult rv = request->Clone(this, getter_AddRefs(clonedRequest));
mInClone = false;
if (NS_FAILED(rv)) {
return;
}
aImage->mRequests.Put(nsnull, request);
aImage->mRequests.Put(mDocument, clonedRequest);
AddImage(aImage);
}
void
ImageLoader::AddImage(nsCSSValue::Image* aImage)
{
NS_ASSERTION(!mImages.Contains(aImage), "Huh?");
if (!mImages.PutEntry(aImage)) {
NS_RUNTIMEABORT("OOM");
}
}
void
ImageLoader::RemoveImage(nsCSSValue::Image* aImage)
{
NS_ASSERTION(mImages.Contains(aImage), "Huh?");
mImages.RemoveEntry(aImage);
}
nsPresContext*
ImageLoader::GetPresContext()
{
if (!mDocument) {
return nsnull;
}
nsIPresShell* shell = mDocument->GetShell();
if (!shell) {
return nsnull;
}
return shell->GetPresContext();
}
void
ImageLoader::DoRedraw(FrameSet* aFrameSet)
{
NS_ASSERTION(aFrameSet, "Must have a frame set");
NS_ASSERTION(mDocument, "Should have returned earlier!");
NS_ASSERTION(mHavePainted, "Should have returned earlier!");
FrameSet::size_type length = aFrameSet->Length();
for (FrameSet::size_type i = 0; i < length; i++) {
nsIFrame* frame = aFrameSet->ElementAt(i);
// 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...
nsRect bounds(nsPoint(0, 0), frame->GetSize());
if (frame->GetType() == nsGkAtoms::canvasFrame) {
// The canvas's background covers the whole viewport.
bounds = frame->GetVisualOverflowRect();
}
if (frame->GetStyleVisibility()->IsVisible()) {
frame->Invalidate(bounds);
}
}
}
NS_IMPL_ADDREF(ImageLoader)
NS_IMPL_RELEASE(ImageLoader)
NS_INTERFACE_MAP_BEGIN(ImageLoader)
NS_INTERFACE_MAP_ENTRY(imgIDecoderObserver)
NS_INTERFACE_MAP_ENTRY(imgIContainerObserver)
NS_INTERFACE_MAP_END
NS_IMETHODIMP
ImageLoader::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage)
{
nsPresContext* presContext = GetPresContext();
if (!presContext) {
return NS_OK;
}
aImage->SetAnimationMode(presContext->ImageAnimationMode());
return NS_OK;
}
NS_IMETHODIMP
ImageLoader::OnImageIsAnimated(imgIRequest* aRequest)
{
// NB: Don't ignore this when cloning, it's our only chance to register
// the request with the refresh driver.
if (!mDocument) {
return NS_OK;
}
// Register with the refresh driver now that we are aware that
// we are animated.
nsPresContext* presContext = GetPresContext();
if (presContext) {
nsLayoutUtils::RegisterImageRequest(presContext,
aRequest,
nsnull);
}
return NS_OK;
}
NS_IMETHODIMP
ImageLoader::OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame)
{
if (!mDocument || !mHavePainted || mInClone) {
return NS_OK;
}
FrameSet* frameSet = nsnull;
if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
return NS_OK;
}
NS_ASSERTION(frameSet, "This should never be null!");
DoRedraw(frameSet);
return NS_OK;
}
NS_IMETHODIMP
ImageLoader::FrameChanged(imgIRequest *aRequest,
imgIContainer *aContainer,
const nsIntRect *aDirtyRect)
{
if (!mDocument || !mHavePainted || mInClone) {
return NS_OK;
}
FrameSet* frameSet = nsnull;
if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
return NS_OK;
}
NS_ASSERTION(frameSet, "This should never be null!");
DoRedraw(frameSet);
return NS_OK;
}
} // namespace css
} // namespace mozilla

128
layout/style/ImageLoader.h Normal file
View File

@ -0,0 +1,128 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// A class that handles style system image loads (other image loads are handled
// by the nodes in the content tree).
#include "nsAutoPtr.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "nsInterfaceHashtable.h"
#include "nsCSSValue.h"
#include "imgIRequest.h"
#include "nsStubImageDecoderObserver.h"
class nsIFrame;
class nsIDocument;
class nsPresContext;
class nsIURI;
class nsIPrincipal;
namespace mozilla {
namespace css {
class ImageLoader : public nsStubImageDecoderObserver {
public:
ImageLoader(nsIDocument* aDocument)
: mDocument(aDocument),
mHavePainted(false),
mInClone(false)
{
MOZ_ASSERT(mDocument);
mRequestToFrameMap.Init();
mFrameToRequestMap.Init();
mImages.Init();
}
NS_DECL_ISUPPORTS
// imgIDecoderObserver (override nsStubImageDecoderObserver)
NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
NS_IMETHOD OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame);
NS_IMETHOD OnImageIsAnimated(imgIRequest *aRequest);
// Do not override OnDataAvailable since background images are not
// displayed incrementally; they are displayed after the entire image
// has been loaded.
// imgIContainerObserver (override nsStubImageDecoderObserver)
NS_IMETHOD FrameChanged(imgIRequest* aRequest,
imgIContainer *aContainer,
const nsIntRect *aDirtyRect);
inline void NotifyPaint()
{
mHavePainted = true;
}
void DropDocumentReference();
void MaybeRegisterCSSImage(nsCSSValue::Image* aImage);
void DeregisterCSSImage(nsCSSValue::Image* aImage);
void AssociateRequestToFrame(imgIRequest* aRequest,
nsIFrame* aFrame);
void DisassociateRequestFromFrame(imgIRequest* aRequest,
nsIFrame* aFrame);
void DropRequestsForFrame(nsIFrame* aFrame);
void SetAnimationMode(PRUint16 aMode);
void ClearAll();
void LoadImage(nsIURI* aURI, nsIPrincipal* aPrincipal, nsIURI* aReferrer,
nsCSSValue::Image* aCSSValue);
void DestroyRequest(imgIRequest* aRequest);
private:
// We need to be able to look up the frames associated with a request (for
// delivering notifications) and the requests associated with a frame (when
// the frame goes away). Thus we maintain hashtables going both ways. These
// should always be in sync.
typedef nsTArray<nsIFrame*> FrameSet;
typedef nsTArray<nsCOMPtr<imgIRequest> > RequestSet;
typedef nsTHashtable<nsPtrHashKey<nsCSSValue::Image> > ImageHashSet;
typedef nsClassHashtable<nsISupportsHashKey,
FrameSet> RequestToFrameMap;
typedef nsClassHashtable<nsPtrHashKey<nsIFrame>,
RequestSet> FrameToRequestMap;
void AddImage(nsCSSValue::Image* aCSSImage);
void RemoveImage(nsCSSValue::Image* aCSSImage);
nsPresContext* GetPresContext();
void DoRedraw(FrameSet* aFrameSet);
static PLDHashOperator
SetAnimationModeEnumerator(nsISupports* aKey, FrameSet* aValue,
void* aClosure);
// A map of imgIRequests to the nsIFrames that are using them.
RequestToFrameMap mRequestToFrameMap;
// A map of nsIFrames to the imgIRequests they use.
FrameToRequestMap mFrameToRequestMap;
// A weak pointer to our document. Nulled out by DropDocumentReference.
nsIDocument* mDocument;
// The set of all nsCSSValue::Images (whether they're associated a frame or
// not). We'll need this when we go away to remove any requests associated
// with our document from those Images.
ImageHashSet mImages;
// Have we painted yet? If not, no need to deliver notifications.
bool mHavePainted;
// Are we cloning? If so, ignore any notifications we get.
bool mInClone;
};
} // namespace css
} // namespace mozilla

View File

@ -72,6 +72,7 @@ EXPORTS = \
EXPORTS_mozilla/css = \
Declaration.h \
GroupRule.h \
ImageLoader.h \
ImportRule.h \
Loader.h \
NameSpaceRule.h \
@ -85,6 +86,7 @@ CPPSRCS = \
nsCSSDataBlock.cpp \
Declaration.cpp \
nsCSSKeywords.cpp \
ImageLoader.cpp \
Loader.cpp \
nsAnimationManager.cpp \
nsCSSParser.cpp \

View File

@ -10,6 +10,7 @@
#include "nsCSSDataBlock.h"
#include "mozilla/css/Declaration.h"
#include "mozilla/css/ImageLoader.h"
#include "nsRuleData.h"
#include "nsStyleSet.h"
#include "nsStyleContext.h"
@ -47,16 +48,25 @@ ShouldIgnoreColors(nsRuleData *aRuleData)
static void
TryToStartImageLoadOnValue(const nsCSSValue& aValue, nsIDocument* aDocument)
{
MOZ_ASSERT(aDocument);
if (aValue.GetUnit() == eCSSUnit_URL) {
aValue.StartImageLoad(aDocument);
}
else if (aValue.GetUnit() == eCSSUnit_Image) {
// If we already have a request, see if this document needs to clone it.
imgIRequest* request = aValue.GetImageValue(nsnull);
if (request) {
aDocument->StyleImageLoader()->MaybeRegisterCSSImage(aValue.GetImageStructValue());
}
}
else if (aValue.EqualsFunction(eCSSKeyword__moz_image_rect)) {
nsCSSValue::Array* arguments = aValue.GetArrayValue();
NS_ABORT_IF_FALSE(arguments->Count() == 6, "unexpected num of arguments");
const nsCSSValue& image = arguments->Item(1);
if (image.GetUnit() == eCSSUnit_URL)
image.StartImageLoad(aDocument);
TryToStartImageLoadOnValue(image, aDocument);
}
}

View File

@ -15,6 +15,7 @@
#include "nsStyleUtil.h"
#include "CSSCalc.h"
#include "nsNetUtil.h"
#include "mozilla/css/ImageLoader.h"
namespace css = mozilla::css;
@ -240,10 +241,10 @@ double nsCSSValue::GetAngleValueInRadians() const
}
}
imgIRequest* nsCSSValue::GetImageValue() const
imgIRequest* nsCSSValue::GetImageValue(nsIDocument* aDocument) const
{
NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Image, "not an Image value");
return mValue.mImage->mRequest;
return mValue.mImage->mRequests.GetWeak(aDocument);
}
nscoord nsCSSValue::GetFixedLength(nsPresContext* aPresContext) const
@ -1701,17 +1702,41 @@ nsCSSValue::Image::Image(nsIURI* aURI, nsStringBuffer* aString,
if (aDocument->GetOriginalDocument()) {
aDocument = aDocument->GetOriginalDocument();
}
if (aURI &&
nsContentUtils::CanLoadImage(aURI, aDocument, aDocument,
aOriginPrincipal)) {
nsContentUtils::LoadImage(aURI, aDocument, aOriginPrincipal, aReferrer,
nullptr, nsIRequest::LOAD_NORMAL,
getter_AddRefs(mRequest));
mRequests.Init();
aDocument->StyleImageLoader()->LoadImage(aURI, aOriginPrincipal, aReferrer,
this);
}
static PLDHashOperator
ClearRequestHashtable(nsISupports* aKey, nsCOMPtr<imgIRequest>& aValue,
void* aClosure)
{
nsCSSValue::Image* image = static_cast<nsCSSValue::Image*>(aClosure);
nsIDocument* doc = static_cast<nsIDocument*>(aKey);
#ifdef DEBUG
{
nsCOMPtr<nsIDocument> slowDoc = do_QueryInterface(aKey);
MOZ_ASSERT(slowDoc == doc);
}
#endif
if (doc) {
doc->StyleImageLoader()->DeregisterCSSImage(image);
}
if (aValue) {
aValue->CancelAndForgetObserver(NS_BINDING_ABORTED);
}
return PL_DHASH_REMOVE;
}
nsCSSValue::Image::~Image()
{
mRequests.Enumerate(&ClearRequestHashtable, this);
}
nsCSSValueGradientStop::nsCSSValueGradientStop()

View File

@ -16,6 +16,7 @@
#include "nsCSSProperty.h"
#include "nsColor.h"
#include "nsCoord.h"
#include "nsInterfaceHashtable.h"
#include "nsString.h"
#include "nsStringBuffer.h"
#include "nsTArray.h"
@ -26,6 +27,8 @@ class nsIDocument;
class nsIPrincipal;
class nsPresContext;
class nsIURI;
template <class T>
class nsPtrHashKey;
// Deletes a linked list iteratively to avoid blowing up the stack (bug 456196).
#define NS_CSS_DELETE_LIST_MEMBER(type_, ptr_, member_) \
@ -354,6 +357,12 @@ public:
return mValue.mURL;
}
Image* GetImageStructValue() const
{
NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Image, "not an Image value");
return mValue.mImage;
}
const PRUnichar* GetOriginalURLValue() const
{
NS_ABORT_IF_FALSE(mUnit == eCSSUnit_URL || mUnit == eCSSUnit_Image,
@ -366,7 +375,7 @@ public:
// Not making this inline because that would force us to include
// imgIRequest.h, which leads to REQUIRES hell, since this header is included
// all over.
imgIRequest* GetImageValue() const;
imgIRequest* GetImageValue(nsIDocument* aDocument) const;
nscoord GetFixedLength(nsPresContext* aPresContext) const;
nscoord GetPixelLength() const;
@ -485,7 +494,7 @@ public:
// Inherit operator== from nsCSSValue::URL
nsCOMPtr<imgIRequest> mRequest; // null == image load blocked or somehow failed
nsInterfaceHashtable<nsISupportsHashKey, imgIRequest> mRequests;
// Override AddRef and Release to not only log ourselves correctly, but
// also so that we delete correctly without a virtual destructor

View File

@ -64,6 +64,12 @@ using namespace mozilla::dom;
method_(req); \
}
#define NS_SET_IMAGE_REQUEST_WITH_DOC(method_, context_, requestgetter_) \
{ \
nsIDocument* doc = (context_)->PresContext()->Document(); \
NS_SET_IMAGE_REQUEST(method_, context_, requestgetter_(doc)) \
}
/*
* For storage of an |nsRuleNode|'s children in a PLDHashTable.
*/
@ -937,9 +943,9 @@ static void SetStyleImageToImageRect(nsStyleContext* aStyleContext,
// <uri>
if (arr->Item(1).GetUnit() == eCSSUnit_Image) {
NS_SET_IMAGE_REQUEST(aResult.SetImageData,
aStyleContext,
arr->Item(1).GetImageValue())
NS_SET_IMAGE_REQUEST_WITH_DOC(aResult.SetImageData,
aStyleContext,
arr->Item(1).GetImageValue)
} else {
NS_WARNING("nsCSSValue::Image::Image() failed?");
}
@ -969,9 +975,9 @@ static void SetStyleImage(nsStyleContext* aStyleContext,
switch (aValue.GetUnit()) {
case eCSSUnit_Image:
NS_SET_IMAGE_REQUEST(aResult.SetImageData,
aStyleContext,
aValue.GetImageValue())
NS_SET_IMAGE_REQUEST_WITH_DOC(aResult.SetImageData,
aStyleContext,
aValue.GetImageValue)
break;
case eCSSUnit_Function:
if (aValue.EqualsFunction(eCSSKeyword__moz_image_rect)) {
@ -3947,9 +3953,10 @@ nsRuleNode::ComputeUserInterfaceData(void* aStartStruct,
cursorUnit).get());
const nsCSSValueList* list = cursorValue->GetListValue();
const nsCSSValueList* list2 = list;
nsIDocument* doc = aContext->PresContext()->Document();
PRUint32 arrayLength = 0;
for ( ; list->mValue.GetUnit() == eCSSUnit_Array; list = list->mNext)
if (list->mValue.GetArrayValue()->Item(0).GetImageValue())
if (list->mValue.GetArrayValue()->Item(0).GetImageValue(doc))
++arrayLength;
if (arrayLength != 0) {
@ -3961,7 +3968,7 @@ nsRuleNode::ComputeUserInterfaceData(void* aStartStruct,
list2->mValue.GetUnit() == eCSSUnit_Array;
list2 = list2->mNext) {
nsCSSValue::Array *arr = list2->mValue.GetArrayValue();
imgIRequest *req = arr->Item(0).GetImageValue();
imgIRequest *req = arr->Item(0).GetImageValue(doc);
if (req) {
item->SetImage(req);
if (arr->Item(1).GetUnit() != eCSSUnit_Null) {
@ -6046,8 +6053,9 @@ nsRuleNode::ComputeBorderData(void* aStartStruct,
// border-image-source
const nsCSSValue* borderImageSource = aRuleData->ValueForBorderImageSource();
if (borderImageSource->GetUnit() == eCSSUnit_Image) {
NS_SET_IMAGE_REQUEST(border->SetBorderImage, aContext,
borderImageSource->GetImageValue());
NS_SET_IMAGE_REQUEST_WITH_DOC(border->SetBorderImage,
aContext,
borderImageSource->GetImageValue);
} else if (borderImageSource->GetUnit() == eCSSUnit_Inherit) {
canStoreInRuleTree = false;
NS_SET_IMAGE_REQUEST(border->SetBorderImage, aContext,
@ -6301,9 +6309,9 @@ nsRuleNode::ComputeListData(void* aStartStruct,
// list-style-image: url, none, inherit
const nsCSSValue* imageValue = aRuleData->ValueForListStyleImage();
if (eCSSUnit_Image == imageValue->GetUnit()) {
NS_SET_IMAGE_REQUEST(list->SetListStyleImage,
aContext,
imageValue->GetImageValue())
NS_SET_IMAGE_REQUEST_WITH_DOC(list->SetListStyleImage,
aContext,
imageValue->GetImageValue)
}
else if (eCSSUnit_None == imageValue->GetUnit() ||
eCSSUnit_Initial == imageValue->GetUnit()) {
@ -6727,7 +6735,9 @@ nsRuleNode::ComputeContentData(void* aStartStruct,
}
data.mType = type;
if (type == eStyleContentType_Image) {
NS_SET_IMAGE_REQUEST(data.SetImage, aContext, value.GetImageValue());
NS_SET_IMAGE_REQUEST_WITH_DOC(data.SetImage,
aContext,
value.GetImageValue);
}
else if (type <= eStyleContentType_Attr) {
value.GetStringValue(buffer);