Bug 683290: Discard images that are not in the DOM. r=bz

This commit is contained in:
Kyle Huey 2012-08-13 15:11:50 -07:00
parent f8ba220f4b
commit e3dadea704
13 changed files with 178 additions and 63 deletions

View File

@ -34,7 +34,7 @@ interface nsIFrame;
* sufficient, when combined with the imageBlockingStatus information.)
*/
[scriptable, uuid(f7debb84-2854-4731-a57b-1bd752ad71f8)]
[scriptable, uuid(4bf1a7c5-6edb-4191-a257-e31a90f6aa85)]
interface nsIImageLoadingContent : imgIDecoderObserver
{
/**
@ -156,9 +156,4 @@ interface nsIImageLoadingContent : imgIDecoderObserver
* as PR_FALSE to revert ImageState() to its original behaviour.
*/
void forceImageState(in boolean aForce, in unsigned long long aState);
/**
* We need to be notified when our document changes.
*/
[noscript, notxpcom] void NotifyOwnerDocumentChanged(in nsIDocument aOldDoc);
};

View File

@ -35,6 +35,10 @@ public:
}
// nsIContent overrides
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers);
virtual void UnbindFromTree(bool aDeep, bool aNullParent);
virtual nsEventStates IntrinsicState() const;
private:
@ -67,6 +71,28 @@ nsGenConImageContent::~nsGenConImageContent()
DestroyImageLoadingContent();
}
nsresult
nsGenConImageContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
nsresult rv;
rv = nsXMLElement::BindToTree(aDocument, aParent, aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
aCompileEventHandlers);
return NS_OK;
}
void
nsGenConImageContent::UnbindFromTree(bool aDeep, bool aNullParent)
{
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
nsXMLElement::UnbindFromTree(aDeep, aNullParent);
}
nsEventStates
nsGenConImageContent::IntrinsicState() const
{

View File

@ -72,7 +72,9 @@ static void PrintReqURL(imgIRequest* req) {
nsImageLoadingContent::nsImageLoadingContent()
: mObserverList(nullptr),
: mCurrentRequestFlags(0),
mPendingRequestFlags(0),
mObserverList(nullptr),
mImageBlockingStatus(nsIContentPolicy::ACCEPT),
mLoadingEnabled(true),
mIsImageStateForced(false),
@ -83,8 +85,6 @@ nsImageLoadingContent::nsImageLoadingContent()
mSuppressed(false),
mBlockingOnload(false),
mNewRequestsWillNeedAnimationReset(false),
mPendingRequestNeedsResetAnimation(false),
mCurrentRequestNeedsResetAnimation(false),
mStateChangerDepth(0),
mCurrentRequestRegistered(false),
mPendingRequestRegistered(false)
@ -292,7 +292,9 @@ nsImageLoadingContent::OnStopDecode(imgIRequest* aRequest,
// initial paint delay (for example, being in the bfcache), but we probably
// aren't loading images in those situations.
nsIDocument* doc = GetOurDocument();
// XXXkhuey should this be GetOurCurrentDoc? Decoding if we're not in
// the document seems silly.
nsIDocument* doc = GetOurOwnerDoc();
nsIPresShell* shell = doc ? doc->GetShell() : nullptr;
if (shell && shell->IsVisible() &&
(!shell->DidInitialReflow() || shell->IsPaintingSuppressed())) {
@ -551,7 +553,7 @@ nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
return NS_ERROR_NULL_POINTER;
}
nsCOMPtr<nsIDocument> doc = GetOurDocument();
nsCOMPtr<nsIDocument> doc = GetOurOwnerDoc();
if (!doc) {
// Don't bother
return NS_OK;
@ -600,29 +602,13 @@ NS_IMETHODIMP nsImageLoadingContent::ForceReload()
* Non-interface methods
*/
void
nsImageLoadingContent::NotifyOwnerDocumentChanged(nsIDocument *aOldDoc)
{
// If we had a document before, unregister ourselves with it.
if (aOldDoc) {
if (mCurrentRequest)
aOldDoc->RemoveImage(mCurrentRequest);
if (mPendingRequest)
aOldDoc->RemoveImage(mPendingRequest);
}
// Re-track the images
TrackImage(mCurrentRequest);
TrackImage(mPendingRequest);
}
nsresult
nsImageLoadingContent::LoadImage(const nsAString& aNewURI,
bool aForce,
bool aNotify)
{
// First, get a document (needed for security checks and the like)
nsIDocument* doc = GetOurDocument();
nsIDocument* doc = GetOurOwnerDoc();
if (!doc) {
// No reason to bother, I think...
return NS_OK;
@ -670,11 +656,11 @@ nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
return NS_OK;
}
NS_ASSERTION(!aDocument || aDocument == GetOurDocument(),
NS_ASSERTION(!aDocument || aDocument == GetOurOwnerDoc(),
"Bogus document passed in");
// First, get a document (needed for security checks and the like)
if (!aDocument) {
aDocument = GetOurDocument();
aDocument = GetOurOwnerDoc();
if (!aDocument) {
// No reason to bother, I think...
return NS_OK;
@ -882,7 +868,7 @@ nsImageLoadingContent::UseAsPrimaryRequest(imgIRequest* aRequest,
}
nsIDocument*
nsImageLoadingContent::GetOurDocument()
nsImageLoadingContent::GetOurOwnerDoc()
{
nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
NS_ENSURE_TRUE(thisContent, nullptr);
@ -890,6 +876,15 @@ nsImageLoadingContent::GetOurDocument()
return thisContent->OwnerDoc();
}
nsIDocument*
nsImageLoadingContent::GetOurCurrentDoc()
{
nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
NS_ENSURE_TRUE(thisContent, nullptr);
return thisContent->GetCurrentDoc();
}
nsIFrame*
nsImageLoadingContent::GetOurPrimaryFrame()
{
@ -996,7 +991,9 @@ nsImageLoadingContent::PrepareCurrentRequest()
// Get rid of anything that was there previously.
ClearCurrentRequest(NS_ERROR_IMAGE_SRC_CHANGED);
mCurrentRequestNeedsResetAnimation = mNewRequestsWillNeedAnimationReset;
if (mNewRequestsWillNeedAnimationReset) {
mCurrentRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET;
}
// Return a reference.
return mCurrentRequest;
@ -1008,7 +1005,9 @@ nsImageLoadingContent::PreparePendingRequest()
// Get rid of anything that was there previously.
ClearPendingRequest(NS_ERROR_IMAGE_SRC_CHANGED);
mPendingRequestNeedsResetAnimation = mNewRequestsWillNeedAnimationReset;
if (mNewRequestsWillNeedAnimationReset) {
mPendingRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET;
}
// Return a reference.
return mPendingRequest;
@ -1054,8 +1053,8 @@ nsImageLoadingContent::MakePendingRequestCurrent()
PrepareCurrentRequest() = mPendingRequest;
mPendingRequest = nullptr;
mCurrentRequestNeedsResetAnimation = mPendingRequestNeedsResetAnimation;
mPendingRequestNeedsResetAnimation = false;
mCurrentRequestFlags = mPendingRequestFlags;
mPendingRequestFlags = 0;
ResetAnimationIfNeeded();
}
@ -1080,7 +1079,7 @@ nsImageLoadingContent::ClearCurrentRequest(nsresult aReason)
UntrackImage(mCurrentRequest);
mCurrentRequest->CancelAndForgetObserver(aReason);
mCurrentRequest = nullptr;
mCurrentRequestNeedsResetAnimation = false;
mCurrentRequestFlags = 0;
// We only block onload during the decoding of "current" images. This one is
// going away, so we should unblock unconditionally here.
@ -1107,7 +1106,7 @@ nsImageLoadingContent::ClearPendingRequest(nsresult aReason)
UntrackImage(mPendingRequest);
mPendingRequest->CancelAndForgetObserver(aReason);
mPendingRequest = nullptr;
mPendingRequestNeedsResetAnimation = false;
mPendingRequestFlags = 0;
}
bool*
@ -1125,12 +1124,13 @@ nsImageLoadingContent::GetRegisteredFlagForRequest(imgIRequest* aRequest)
void
nsImageLoadingContent::ResetAnimationIfNeeded()
{
if (mCurrentRequest && mCurrentRequestNeedsResetAnimation) {
if (mCurrentRequest &&
(mCurrentRequestFlags & REQUEST_NEEDS_ANIMATION_RESET)) {
nsCOMPtr<imgIContainer> container;
mCurrentRequest->GetImage(getter_AddRefs(container));
if (container)
container->ResetAnimation();
mCurrentRequestNeedsResetAnimation = false;
mCurrentRequestFlags &= ~REQUEST_NEEDS_ANIMATION_RESET;
}
}
@ -1155,7 +1155,7 @@ nsImageLoadingContent::SetBlockingOnload(bool aBlocking)
return;
// Get the document
nsIDocument* doc = GetOurDocument();
nsIDocument* doc = GetOurOwnerDoc();
if (doc) {
// Take the appropriate action
@ -1169,13 +1169,51 @@ nsImageLoadingContent::SetBlockingOnload(bool aBlocking)
}
}
void
nsImageLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
// We may be entering the document, so if our image should be tracked,
// track it.
if (!aDocument)
return;
if (mCurrentRequestFlags & REQUEST_SHOULD_BE_TRACKED)
aDocument->AddImage(mCurrentRequest);
if (mPendingRequestFlags & REQUEST_SHOULD_BE_TRACKED)
aDocument->AddImage(mPendingRequest);
}
void
nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
{
// We may be leaving the document, so if our image is tracked, untrack it.
nsCOMPtr<nsIDocument> doc = GetOurCurrentDoc();
if (!doc)
return;
if (mCurrentRequestFlags & REQUEST_SHOULD_BE_TRACKED)
doc->RemoveImage(mCurrentRequest);
if (mPendingRequestFlags & REQUEST_SHOULD_BE_TRACKED)
doc->RemoveImage(mPendingRequest);
}
nsresult
nsImageLoadingContent::TrackImage(imgIRequest* aImage)
{
if (!aImage)
return NS_OK;
nsIDocument* doc = GetOurDocument();
MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest,
"Why haven't we heard of this request?");
if (aImage == mCurrentRequest) {
mCurrentRequestFlags |= REQUEST_SHOULD_BE_TRACKED;
} else {
mPendingRequestFlags |= REQUEST_SHOULD_BE_TRACKED;
}
nsIDocument* doc = GetOurCurrentDoc();
if (doc)
return doc->AddImage(aImage);
return NS_OK;
@ -1187,10 +1225,18 @@ nsImageLoadingContent::UntrackImage(imgIRequest* aImage)
if (!aImage)
return NS_OK;
MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest,
"Why haven't we heard of this request?");
if (aImage == mCurrentRequest) {
mCurrentRequestFlags &= ~REQUEST_SHOULD_BE_TRACKED;
} else {
mPendingRequestFlags &= ~REQUEST_SHOULD_BE_TRACKED;
}
// If GetOurDocument() returns null here, we've outlived our document.
// That's fine, because the document empties out the tracker and unlocks
// all locked images on destruction.
nsIDocument* doc = GetOurDocument();
nsIDocument* doc = GetOurCurrentDoc();
if (doc)
return doc->RemoveImage(aImage);
return NS_OK;

View File

@ -87,13 +87,14 @@ protected:
nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL);
/**
* helper to get the document for this content (from the nodeinfo
* and such). Not named GetDocument to prevent ambiguous method
* names in subclasses
* helpers to get the document for this content (from the nodeinfo
* and such). Not named GetOwnerDoc/GetCurrentDoc to prevent ambiguous
* method names in subclasses
*
* @return the document we belong to
*/
nsIDocument* GetOurDocument();
nsIDocument* GetOurOwnerDoc();
nsIDocument* GetOurCurrentDoc();
/**
* Helper function to get the frame associated with this content. Not named
@ -152,6 +153,11 @@ protected:
*/
virtual mozilla::CORSMode GetCORSMode();
// Subclasses are *required* to call BindToTree/UnbindFromTree.
void BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent, bool aCompileEventHandlers);
void UnbindFromTree(bool aDeep, bool aNullParent);
private:
/**
* Struct used to manage the image observers.
@ -308,6 +314,16 @@ protected:
/* MEMBERS */
nsCOMPtr<imgIRequest> mCurrentRequest;
nsCOMPtr<imgIRequest> mPendingRequest;
PRUint32 mCurrentRequestFlags;
PRUint32 mPendingRequestFlags;
enum {
// Set if the request needs
REQUEST_NEEDS_ANIMATION_RESET = 0x00000001U,
// Set if the request should be tracked. This is true if the request is
// not tracked iff this node is not in the document.
REQUEST_SHOULD_BE_TRACKED = 0x00000002U
};
// If the image was blocked or if there was an error loading, it's nice to
// still keep track of what the URI was despite not having an imgIRequest.
@ -366,9 +382,6 @@ protected:
bool mNewRequestsWillNeedAnimationReset : 1;
private:
bool mPendingRequestNeedsResetAnimation : 1;
bool mCurrentRequestNeedsResetAnimation : 1;
/* The number of nested AutoStateChangers currently tracking our state. */
PRUint8 mStateChangerDepth;

View File

@ -510,16 +510,9 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
olc->NotifyOwnerDocumentActivityChanged();
}
}
// nsImageLoadingContent needs to know when its document changes
if (oldDoc != newDoc) {
nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(aNode));
if (imageContent) {
imageContent->NotifyOwnerDocumentChanged(oldDoc);
}
if (oldDoc->MayHaveDOMMutationObservers()) {
newDoc->SetMayHaveDOMMutationObservers();
}
if (oldDoc != newDoc && oldDoc->MayHaveDOMMutationObservers()) {
newDoc->SetMayHaveDOMMutationObservers();
}
if (elem) {

View File

@ -605,10 +605,13 @@ nsObjectLoadingContent::IsSupportedDocument(const nsCString& aMimeType)
nsresult
nsObjectLoadingContent::BindToTree(nsIDocument* aDocument,
nsIContent* /*aParent*/,
nsIContent* /*aBindingParent*/,
bool /*aCompileEventHandlers*/)
nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
aCompileEventHandlers);
if (aDocument) {
return aDocument->AddPlugin(this);
}
@ -616,8 +619,10 @@ nsObjectLoadingContent::BindToTree(nsIDocument* aDocument,
}
void
nsObjectLoadingContent::UnbindFromTree(bool /*aDeep*/, bool /*aNullParent*/)
nsObjectLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
{
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
nsCOMPtr<nsIContent> thisContent =
do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
MOZ_ASSERT(thisContent);

View File

@ -407,6 +407,9 @@ nsHTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
aCompileEventHandlers);
if (HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
// FIXME: Bug 660963 it would be nice if we could just have
// ClearBrokenState update our state and do it fast...
@ -424,6 +427,13 @@ nsHTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
return rv;
}
void
nsHTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
}
void
nsHTMLImageElement::MaybeLoadImage()
{

View File

@ -91,6 +91,7 @@ public:
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers);
virtual void UnbindFromTree(bool aDeep, bool aNullParent);
virtual nsEventStates IntrinsicState() const;
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;

View File

@ -2495,6 +2495,9 @@ nsHTMLInputElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
aCompileEventHandlers);
if (mType == NS_FORM_INPUT_IMAGE) {
// Our base URI may have changed; claim that our URI changed, and the
// nsImageLoadingContent will decide whether a new image load is warranted.
@ -2541,6 +2544,7 @@ nsHTMLInputElement::UnbindFromTree(bool aDeep, bool aNullParent)
WillRemoveFromRadioGroup();
}
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent);
// GetCurrentDoc is returning nullptr so we can update the value

View File

@ -5604,6 +5604,9 @@ nsSVGFEImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
aCompileEventHandlers);
if (mStringAttributes[HREF].IsExplicitlySet()) {
// FIXME: Bug 660963 it would be nice if we could just have
// ClearBrokenState update our state and do it fast...
@ -5615,6 +5618,13 @@ nsSVGFEImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
return rv;
}
void
nsSVGFEImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
nsSVGFEImageElementBase::UnbindFromTree(aDeep, aNullParent);
}
nsEventStates
nsSVGFEImageElement::IntrinsicState() const

View File

@ -264,6 +264,7 @@ public:
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers);
virtual void UnbindFromTree(bool aDeep, bool aNullParent);
virtual nsEventStates IntrinsicState() const;
// imgIDecoderObserver

View File

@ -183,6 +183,9 @@ nsSVGImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
aCompileEventHandlers);
if (mStringAttributes[HREF].IsExplicitlySet()) {
// FIXME: Bug 660963 it would be nice if we could just have
// ClearBrokenState update our state and do it fast...
@ -195,6 +198,13 @@ nsSVGImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
return rv;
}
void
nsSVGImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
nsSVGImageElementBase::UnbindFromTree(aDeep, aNullParent);
}
nsEventStates
nsSVGImageElement::IntrinsicState() const
{

View File

@ -49,6 +49,7 @@ public:
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers);
virtual void UnbindFromTree(bool aDeep, bool aNullParent);
virtual nsEventStates IntrinsicState() const;