зеркало из https://github.com/mozilla/gecko-dev.git
Bug 666446, Part 4/18 - Implement refresh driver based animations in nsImageLoadingContent to improve efficiency of animated GIF images. [r=roc][sr=mats]
This commit is contained in:
Родитель
6b33216408
Коммит
48e6539470
|
@ -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.
|
||||
*/
|
||||
void frameCreated(in nsIFrame aFrame);
|
||||
|
||||
/**
|
||||
* Used to notify the image loading content node that a frame has been
|
||||
* destroyed.
|
||||
*/
|
||||
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
|
||||
|
|
|
@ -3220,6 +3220,7 @@ nsDocument::DeleteShell()
|
|||
if (IsEventHandlingEnabled()) {
|
||||
RevokeAnimationFrameNotifications();
|
||||
}
|
||||
|
||||
mPresShell = nsnull;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
#include "nsIDOMNode.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsContentPolicyUtils.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
|
@ -116,7 +117,9 @@ nsImageLoadingContent::nsImageLoadingContent()
|
|||
mNewRequestsWillNeedAnimationReset(PR_FALSE),
|
||||
mPendingRequestNeedsResetAnimation(PR_FALSE),
|
||||
mCurrentRequestNeedsResetAnimation(PR_FALSE),
|
||||
mStateChangerDepth(0)
|
||||
mStateChangerDepth(0),
|
||||
mCurrentRequestRegistered(false),
|
||||
mPendingRequestRegistered(false)
|
||||
{
|
||||
if (!nsContentUtils::GetImgLoader()) {
|
||||
mLoadingEnabled = PR_FALSE;
|
||||
|
@ -330,6 +333,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;
|
||||
|
||||
|
@ -502,6 +512,48 @@ nsImageLoadingContent::GetRequest(PRInt32 aRequestType,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
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);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
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);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
|
||||
|
@ -868,6 +920,23 @@ nsImageLoadingContent::GetOurDocument()
|
|||
return thisContent->GetOwnerDoc();
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -987,6 +1056,11 @@ 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);
|
||||
|
||||
// Clean up the request.
|
||||
UntrackImage(mCurrentRequest);
|
||||
mCurrentRequest->CancelAndForgetObserver(aReason);
|
||||
|
@ -1010,12 +1084,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 = PR_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)
|
||||
{
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -576,6 +576,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;
|
||||
|
|
|
@ -223,6 +223,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);
|
||||
}
|
||||
|
||||
|
@ -268,6 +272,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,
|
||||
|
|
|
@ -100,6 +100,7 @@ public:
|
|||
NS_IMETHOD Init(nsIContent* aContent,
|
||||
nsIFrame* aParent,
|
||||
nsIFrame* aPrevInFlow);
|
||||
virtual void DestroyFrom(nsIFrame* aDestructRoot);
|
||||
|
||||
/**
|
||||
* Get the "type" of the frame
|
||||
|
@ -176,6 +177,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.
|
||||
|
@ -187,6 +192,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:
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче