Bug 666446, Part 4/10 - Implement refresh driver based animations in nsImageLoadingContent to improve efficiency of animated GIF images. [r=roc][sr=mats]

This commit is contained in:
Scott Johnson 2011-11-01 11:06:53 -04:00
parent f67db42a56
commit ae7cdb99f9
8 changed files with 215 additions and 3 deletions

View File

@ -42,6 +42,7 @@ interface nsIChannel;
interface nsIStreamListener;
interface nsIURI;
interface nsIDocument;
interface nsIFrame;
/**
* This interface represents a content node that loads images. The interface
@ -65,7 +66,7 @@ interface nsIDocument;
* sufficient, when combined with the imageBlockingStatus information.)
*/
[scriptable, uuid(95c74255-df9a-4060-b5a0-0d111fcafe08)]
[scriptable, uuid(f7debb84-2854-4731-a57b-1bd752ad71f8)]
interface nsIImageLoadingContent : imgIDecoderObserver
{
/**
@ -128,6 +129,18 @@ interface nsIImageLoadingContent : imgIDecoderObserver
*/
imgIRequest getRequest(in long aRequestType);
/**
* Used to notify the image loading content node that a frame has been
* created.
*/
[notxpcom] void frameCreated(in nsIFrame aFrame);
/**
* Used to notify the image loading content node that a frame has been
* destroyed.
*/
[notxpcom] void frameDestroyed(in nsIFrame aFrame);
/**
* Used to find out what type of request one is dealing with (eg
* which request got passed through to the imgIDecoderObserver

View File

@ -3225,6 +3225,7 @@ nsDocument::DeleteShell()
if (IsEventHandlingEnabled()) {
RevokeAnimationFrameNotifications();
}
mPresShell = nsnull;
}

View File

@ -72,6 +72,7 @@
#include "nsIDOMNode.h"
#include "nsContentUtils.h"
#include "nsLayoutUtils.h"
#include "nsIContentPolicy.h"
#include "nsContentPolicyUtils.h"
#include "nsEventDispatcher.h"
@ -115,7 +116,9 @@ nsImageLoadingContent::nsImageLoadingContent()
mNewRequestsWillNeedAnimationReset(false),
mPendingRequestNeedsResetAnimation(false),
mCurrentRequestNeedsResetAnimation(false),
mStateChangerDepth(0)
mStateChangerDepth(0),
mCurrentRequestRegistered(false),
mPendingRequestRegistered(false)
{
if (!nsContentUtils::GetImgLoader()) {
mLoadingEnabled = false;
@ -196,7 +199,16 @@ nsImageLoadingContent::OnStartDecode(imgIRequest* aRequest)
SetBlockingOnload(true);
}
}
bool* requestFlag = GetRegisteredFlagForRequest(aRequest);
if (requestFlag) {
nsLayoutUtils::RegisterImageRequest(GetFramePresContext(), aRequest,
requestFlag);
} else {
NS_ERROR("Starting to decode an image other than our current/pending "
"request?");
}
LOOP_OVER_OBSERVERS(OnStartDecode(aRequest));
return NS_OK;
}
@ -329,6 +341,13 @@ nsImageLoadingContent::OnStopDecode(imgIRequest* aRequest,
nsIPresShell* shell = doc ? doc->GetShell() : nsnull;
if (shell) {
// Make sure that our image requests are deregistered from the refresh
// driver if they aren't animated. Note that this must be mCurrentRequest,
// or we would have aborted up above.
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(GetFramePresContext(),
mCurrentRequest,
&mCurrentRequestRegistered);
// We need to figure out whether to kick off decoding
bool doRequestDecode = false;
@ -501,6 +520,44 @@ nsImageLoadingContent::GetRequest(PRInt32 aRequestType,
return NS_OK;
}
NS_IMETHODIMP_(void)
nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
{
NS_ASSERTION(aFrame, "aFrame is null");
// We need to make sure that our image request is registered.
nsPresContext* presContext = aFrame->PresContext();
if (mCurrentRequest) {
nsLayoutUtils::RegisterImageRequest(presContext, mCurrentRequest,
&mCurrentRequestRegistered);
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(presContext,
mCurrentRequest,
&mCurrentRequestRegistered);
} else if (mPendingRequest) {
// We don't need to do the same check for animation, because this will be
// done when decoding is finished.
nsLayoutUtils::RegisterImageRequest(presContext, mPendingRequest,
&mPendingRequestRegistered);
}
}
NS_IMETHODIMP_(void)
nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame)
{
NS_ASSERTION(aFrame, "aFrame is null");
// We need to make sure that our image request is deregistered.
if (mCurrentRequest) {
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(),
mCurrentRequest,
&mCurrentRequestRegistered);
} else if (mPendingRequest) {
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(),
mPendingRequest,
&mPendingRequestRegistered);
}
}
NS_IMETHODIMP
nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
@ -867,6 +924,23 @@ nsImageLoadingContent::GetOurDocument()
return thisContent->OwnerDoc();
}
nsIFrame*
nsImageLoadingContent::GetOurPrimaryFrame()
{
nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
return thisContent->GetPrimaryFrame();
}
nsPresContext* nsImageLoadingContent::GetFramePresContext()
{
nsIFrame* frame = GetOurPrimaryFrame();
if (!frame) {
return nsnull;
}
return frame->PresContext();
}
nsresult
nsImageLoadingContent::StringToURI(const nsAString& aSpec,
nsIDocument* aDocument,
@ -986,6 +1060,16 @@ nsImageLoadingContent::ClearCurrentRequest(nsresult aReason)
NS_ABORT_IF_FALSE(!mCurrentURI,
"Shouldn't have both mCurrentRequest and mCurrentURI!");
// Deregister this image from the refresh driver so it no longer receives
// notifications.
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mCurrentRequest,
&mCurrentRequestRegistered);
// Deregister this image from the refresh driver so it no longer receives
// notifications.
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mCurrentRequest,
&mCurrentRequestRegistered);
// Clean up the request.
UntrackImage(mCurrentRequest);
mCurrentRequest->CancelAndForgetObserver(aReason);
@ -1009,12 +1093,29 @@ nsImageLoadingContent::ClearPendingRequest(nsresult aReason)
nsCxPusher pusher;
pusher.PushNull();
// Deregister this image from the refresh driver so it no longer receives
// notifications.
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest,
&mPendingRequestRegistered);
UntrackImage(mPendingRequest);
mPendingRequest->CancelAndForgetObserver(aReason);
mPendingRequest = nsnull;
mPendingRequestNeedsResetAnimation = false;
}
bool*
nsImageLoadingContent::GetRegisteredFlagForRequest(imgIRequest* aRequest)
{
if (aRequest == mCurrentRequest) {
return &mCurrentRequestRegistered;
} else if (aRequest == mPendingRequest) {
return &mPendingRequestRegistered;
} else {
return nsnull;
}
}
bool
nsImageLoadingContent::HaveSize(imgIRequest *aImage)
{

View File

@ -146,6 +146,24 @@ protected:
*/
nsIDocument* GetOurDocument();
/**
* Helper function to get the frame associated with this content. Not named
* GetPrimaryFrame to prevent ambiguous method names in subclasses.
*
* @return The frame which we belong to, or nsnull if it doesn't exist.
*/
nsIFrame* GetOurPrimaryFrame();
/**
* Helper function to get the PresContext associated with this content's
* frame. Not named GetPresContext to prevent ambiguous method names in
* subclasses.
*
* @return The nsPresContext associated with our frame, or nsnull if either
* the frame doesn't exist, or the frame's prescontext doesn't exist.
*/
nsPresContext* GetFramePresContext();
/**
* CancelImageRequests is called by subclasses when they want to
* cancel all image requests (for example when the subclass is
@ -302,6 +320,16 @@ protected:
void ClearCurrentRequest(nsresult aReason);
void ClearPendingRequest(nsresult aReason);
/**
* Retrieve a pointer to the 'registered with the refresh driver' flag for
* which a particular image request corresponds.
*
* @returns A pointer to the boolean flag for a given image request, or
* |nsnull| if the request is not either |mPendingRequest| or
* |mCurrentRequest|.
*/
bool* GetRegisteredFlagForRequest(imgIRequest* aRequest);
/**
* Static helper method to tell us if we have the size of a request. The
* image may be null.
@ -382,6 +410,11 @@ private:
/* The number of nested AutoStateChangers currently tracking our state. */
PRUint8 mStateChangerDepth;
// Flags to indicate whether each of the current and pending requests are
// registered with the refresh driver.
bool mCurrentRequestRegistered;
bool mPendingRequestRegistered;
};
#endif // nsImageLoadingContent_h__

View File

@ -579,6 +579,10 @@ protected:
/**
* Implements Destroy(). Do not call this directly except from within a
* DestroyFrom() implementation.
*
* @note This will always be called, so it is not necessary to override
* Destroy() in subclasses of nsFrame, just DestroyFrom().
*
* @param aDestructRoot is the root of the subtree being destroyed
*/
virtual void DestroyFrom(nsIFrame* aDestructRoot) = 0;

View File

@ -231,6 +231,10 @@ nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
nsCxPusher pusher;
pusher.PushNull();
// Notify our image loading content that we are going away so it can
// deregister with our refresh driver.
imageLoader->FrameDestroyed(this);
imageLoader->RemoveObserver(mListener);
}
@ -276,6 +280,10 @@ nsImageFrame::Init(nsIContent* aContent,
if (!gIconLoad)
LoadIcons(aPresContext);
// We have a PresContext now, so we need to notify the image content node
// that it can register images.
imageLoader->FrameCreated(this);
// Give image loads associated with an image frame a small priority boost!
nsCOMPtr<imgIRequest> currentRequest;
imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,

View File

@ -103,6 +103,7 @@ public:
NS_IMETHOD Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow);
virtual void DestroyFrom(nsIFrame* aDestructRoot);
/**
* Get the "type" of the frame
@ -179,6 +180,10 @@ nsSVGImageFrame::Init(nsIContent* aContent,
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
// We should have a PresContext now, so let's notify our image loader that
// we need to register any image animations with the refresh driver.
imageLoader->FrameCreated(this);
// Push a null JSContext on the stack so that code that runs within
// the below code doesn't think it's being called by JS. See bug
// 604262.
@ -190,6 +195,19 @@ nsSVGImageFrame::Init(nsIContent* aContent,
return NS_OK;
}
/* virtual */ void
nsSVGImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
nsCOMPtr<nsIImageLoadingContent> imageLoader =
do_QueryInterface(nsFrame::mContent);
if (imageLoader) {
imageLoader->FrameDestroyed(this);
}
nsFrame::DestroyFrom(aDestructRoot);
}
//----------------------------------------------------------------------
// nsIFrame methods:

View File

@ -36,6 +36,7 @@
#include "nsFrame.h"
#include "nsSVGEffects.h"
#include "nsImageLoadingContent.h"
class nsSVGLeafFrame : public nsFrame
{
@ -47,6 +48,12 @@ protected:
public:
NS_DECL_FRAMEARENA_HELPERS
NS_IMETHOD Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* asPrevInFlow);
virtual void DestroyFrom(nsIFrame* aDestructRoot);
virtual bool IsFrameOfType(PRUint32 aFlags) const
{
return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
@ -70,6 +77,33 @@ NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
NS_IMPL_FRAMEARENA_HELPERS(nsSVGLeafFrame)
NS_IMETHODIMP
nsSVGLeafFrame::Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* asPrevInFlow)
{
nsFrame::Init(aContent, aParent, asPrevInFlow);
nsCOMPtr<nsIImageLoadingContent> imageLoader =
do_QueryInterface(nsFrame::mContent);
if (imageLoader) {
imageLoader->FrameCreated(this);
}
return NS_OK;
}
/* virtual */ void
nsSVGLeafFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
nsCOMPtr<nsIImageLoadingContent> imageLoader =
do_QueryInterface(nsFrame::mContent);
if (imageLoader) {
imageLoader->FrameDestroyed(this);
}
nsFrame::DestroyFrom(aDestructRoot);
}
/* virtual */ void
nsSVGLeafFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
{