зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1157546 - Replace the image visibility API with a more general API that tracks visibility for any kind of frame. r=mstange
This commit is contained in:
Родитель
020510839b
Коммит
5973113f1b
|
@ -877,8 +877,8 @@ pref("memory.system_memory_reporter", true);
|
|||
// Don't dump memory reports on OOM, by default.
|
||||
pref("memory.dump_reports_on_oom", false);
|
||||
|
||||
pref("layout.imagevisibility.numscrollportwidths", 1);
|
||||
pref("layout.imagevisibility.numscrollportheights", 1);
|
||||
pref("layout.framevisibility.numscrollportwidths", 1);
|
||||
pref("layout.framevisibility.numscrollportheights", 1);
|
||||
|
||||
// Enable native identity (persona/browserid)
|
||||
pref("dom.identity.enabled", true);
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
|
||||
#include "imgINotificationObserver.idl"
|
||||
|
||||
%{C++
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "Visibility.h"
|
||||
%}
|
||||
|
||||
interface imgIRequest;
|
||||
interface nsIChannel;
|
||||
interface nsIStreamListener;
|
||||
|
@ -12,6 +17,9 @@ interface nsIURI;
|
|||
interface nsIDocument;
|
||||
interface nsIFrame;
|
||||
|
||||
[ref] native MaybeOnNonvisible(const mozilla::Maybe<mozilla::OnNonvisible>);
|
||||
native Visibility(mozilla::Visibility);
|
||||
|
||||
/**
|
||||
* This interface represents a content node that loads images. The interface
|
||||
* exists to allow getting information on the images that the content node
|
||||
|
@ -32,7 +40,7 @@ interface nsIFrame;
|
|||
* interface to mirror this interface when changing it.
|
||||
*/
|
||||
|
||||
[scriptable, builtinclass, uuid(770f7d84-c917-42d7-bf8d-d1b70649e733)]
|
||||
[scriptable, builtinclass, uuid(0357123d-9224-4d12-a47e-868c32689777)]
|
||||
interface nsIImageLoadingContent : imgINotificationObserver
|
||||
{
|
||||
/**
|
||||
|
@ -169,18 +177,17 @@ interface nsIImageLoadingContent : imgINotificationObserver
|
|||
readonly attribute unsigned long naturalHeight;
|
||||
|
||||
/**
|
||||
* A visible count is stored, if it is non-zero then this image is considered
|
||||
* visible. These methods increment, decrement, or return the visible count.
|
||||
* Called by layout to announce when the frame associated with this content
|
||||
* has changed its visibility state.
|
||||
*
|
||||
* @param aNonvisibleAction What to do if the image's visibility count is now
|
||||
* zero. If ON_NONVISIBLE_NO_ACTION, nothing will be
|
||||
* done. If ON_NONVISIBLE_REQUEST_DISCARD, the image
|
||||
* will be asked to discard its surfaces if possible.
|
||||
* @param aNewVisibility The new visibility state.
|
||||
* @param aNonvisibleAction A requested action if the frame has become
|
||||
* nonvisible. If Nothing(), no action is
|
||||
* requested. If DISCARD_IMAGES is specified, the
|
||||
* frame is requested to ask any images it's
|
||||
* associated with to discard their surfaces if
|
||||
* possible.
|
||||
*/
|
||||
[noscript, notxpcom] void IncrementVisibleCount();
|
||||
[noscript, notxpcom] void DecrementVisibleCount(in uint32_t aNonvisibleAction);
|
||||
[noscript, notxpcom] uint32_t GetVisibleCount();
|
||||
|
||||
const long ON_NONVISIBLE_NO_ACTION = 0;
|
||||
const long ON_NONVISIBLE_REQUEST_DISCARD = 1;
|
||||
[noscript, notxpcom] void onVisibilityChange(in Visibility aNewVisibility,
|
||||
in MaybeOnNonvisible aNonvisibleAction);
|
||||
};
|
||||
|
|
|
@ -94,8 +94,7 @@ nsImageLoadingContent::nsImageLoadingContent()
|
|||
mStateChangerDepth(0),
|
||||
mCurrentRequestRegistered(false),
|
||||
mPendingRequestRegistered(false),
|
||||
mFrameCreateCalled(false),
|
||||
mVisibleCount(0)
|
||||
mFrameCreateCalled(false)
|
||||
{
|
||||
if (!nsContentUtils::GetImgLoaderForChannel(nullptr, nullptr)) {
|
||||
mLoadingEnabled = false;
|
||||
|
@ -110,8 +109,8 @@ nsImageLoadingContent::DestroyImageLoadingContent()
|
|||
{
|
||||
// Cancel our requests so they won't hold stale refs to us
|
||||
// NB: Don't ask to discard the images here.
|
||||
ClearCurrentRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_NO_ACTION);
|
||||
ClearPendingRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_NO_ACTION);
|
||||
ClearCurrentRequest(NS_BINDING_ABORTED);
|
||||
ClearPendingRequest(NS_BINDING_ABORTED);
|
||||
}
|
||||
|
||||
nsImageLoadingContent::~nsImageLoadingContent()
|
||||
|
@ -264,12 +263,7 @@ ImageIsAnimated(imgIRequest* aRequest)
|
|||
void
|
||||
nsImageLoadingContent::OnUnlockedDraw()
|
||||
{
|
||||
if (mVisibleCount > 0) {
|
||||
// We should already be marked as visible, there is nothing more we can do.
|
||||
return;
|
||||
}
|
||||
|
||||
// It's OK for non-animated images to wait until the next image visibility
|
||||
// It's OK for non-animated images to wait until the next frame visibility
|
||||
// update to become locked. (And that's preferable, since in the case of
|
||||
// scrolling it keeps memory usage minimal.) For animated images, though, we
|
||||
// want to mark them visible right away so we can call
|
||||
|
@ -278,15 +272,27 @@ nsImageLoadingContent::OnUnlockedDraw()
|
|||
return;
|
||||
}
|
||||
|
||||
nsPresContext* presContext = GetFramePresContext();
|
||||
if (!presContext)
|
||||
nsIFrame* frame = GetOurPrimaryFrame();
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame->GetVisibility() == Visibility::APPROXIMATELY_VISIBLE) {
|
||||
// This frame is already marked visible; there's nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
nsPresContext* presContext = frame->PresContext();
|
||||
if (!presContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIPresShell* presShell = presContext->PresShell();
|
||||
if (!presShell)
|
||||
if (!presShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
presShell->EnsureImageInVisibleList(this);
|
||||
presShell->EnsureFrameInApproximatelyVisibleList(frame);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -470,11 +476,6 @@ nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
|
|||
|
||||
mFrameCreateCalled = true;
|
||||
|
||||
if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
|
||||
// Assume all images in popups are visible.
|
||||
IncrementVisibleCount();
|
||||
}
|
||||
|
||||
TrackImage(mCurrentRequest);
|
||||
TrackImage(mPendingRequest);
|
||||
|
||||
|
@ -518,13 +519,7 @@ nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame)
|
|||
|
||||
nsIPresShell* presShell = presContext ? presContext->GetPresShell() : nullptr;
|
||||
if (presShell) {
|
||||
presShell->RemoveImageFromVisibleList(this);
|
||||
}
|
||||
|
||||
if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
|
||||
// We assume all images in popups are visible, so this decrement balances
|
||||
// out the increment in FrameCreated above.
|
||||
DecrementVisibleCount(ON_NONVISIBLE_NO_ACTION);
|
||||
presShell->RemoveFrameFromApproximatelyVisibleList(aFrame);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -738,34 +733,6 @@ nsImageLoadingContent::UnblockOnload(imgIRequest* aRequest)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsImageLoadingContent::IncrementVisibleCount()
|
||||
{
|
||||
mVisibleCount++;
|
||||
if (mVisibleCount == 1) {
|
||||
TrackImage(mCurrentRequest);
|
||||
TrackImage(mPendingRequest);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsImageLoadingContent::DecrementVisibleCount(uint32_t aNonvisibleAction)
|
||||
{
|
||||
NS_ASSERTION(mVisibleCount > 0, "visible count should be positive here");
|
||||
mVisibleCount--;
|
||||
|
||||
if (mVisibleCount == 0) {
|
||||
UntrackImage(mCurrentRequest, aNonvisibleAction);
|
||||
UntrackImage(mPendingRequest, aNonvisibleAction);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsImageLoadingContent::GetVisibleCount()
|
||||
{
|
||||
return mVisibleCount;
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-interface methods
|
||||
*/
|
||||
|
@ -1076,8 +1043,8 @@ void
|
|||
nsImageLoadingContent::CancelImageRequests(bool aNotify)
|
||||
{
|
||||
AutoStateChanger changer(this, aNotify);
|
||||
ClearPendingRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_REQUEST_DISCARD);
|
||||
ClearCurrentRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_REQUEST_DISCARD);
|
||||
ClearPendingRequest(NS_BINDING_ABORTED, Some(OnNonvisible::DISCARD_IMAGES));
|
||||
ClearCurrentRequest(NS_BINDING_ABORTED, Some(OnNonvisible::DISCARD_IMAGES));
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -1089,8 +1056,8 @@ nsImageLoadingContent::UseAsPrimaryRequest(imgRequestProxy* aRequest,
|
|||
AutoStateChanger changer(this, aNotify);
|
||||
|
||||
// Get rid if our existing images
|
||||
ClearPendingRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_REQUEST_DISCARD);
|
||||
ClearCurrentRequest(NS_BINDING_ABORTED, ON_NONVISIBLE_REQUEST_DISCARD);
|
||||
ClearPendingRequest(NS_BINDING_ABORTED, Some(OnNonvisible::DISCARD_IMAGES));
|
||||
ClearCurrentRequest(NS_BINDING_ABORTED, Some(OnNonvisible::DISCARD_IMAGES));
|
||||
|
||||
// Clone the request we were given.
|
||||
RefPtr<imgRequestProxy>& req = PrepareNextRequest(aImageLoadType);
|
||||
|
@ -1228,7 +1195,9 @@ nsImageLoadingContent::SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision)
|
|||
// reason "image source changed". However, apparently there's some abuse
|
||||
// over in nsImageFrame where the displaying of the "broken" icon for the
|
||||
// next image depends on the cancel reason of the previous image. ugh.
|
||||
ClearPendingRequest(NS_ERROR_IMAGE_BLOCKED, ON_NONVISIBLE_REQUEST_DISCARD);
|
||||
// XXX(seth): So shouldn't we fix nsImageFrame?!
|
||||
ClearPendingRequest(NS_ERROR_IMAGE_BLOCKED,
|
||||
Some(OnNonvisible::DISCARD_IMAGES));
|
||||
|
||||
// For the blocked case, we only want to cancel the existing current request
|
||||
// if size is not available. bz says the web depends on this behavior.
|
||||
|
@ -1236,7 +1205,8 @@ nsImageLoadingContent::SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision)
|
|||
|
||||
mImageBlockingStatus = aContentDecision;
|
||||
uint32_t keepFlags = mCurrentRequestFlags & REQUEST_IS_IMAGESET;
|
||||
ClearCurrentRequest(NS_ERROR_IMAGE_BLOCKED, ON_NONVISIBLE_REQUEST_DISCARD);
|
||||
ClearCurrentRequest(NS_ERROR_IMAGE_BLOCKED,
|
||||
Some(OnNonvisible::DISCARD_IMAGES));
|
||||
|
||||
// We still want to remember what URI we were and if it was an imageset,
|
||||
// despite not having an actual request. These are both cleared as part of
|
||||
|
@ -1255,7 +1225,7 @@ nsImageLoadingContent::PrepareCurrentRequest(ImageLoadType aImageLoadType)
|
|||
|
||||
// Get rid of anything that was there previously.
|
||||
ClearCurrentRequest(NS_ERROR_IMAGE_SRC_CHANGED,
|
||||
ON_NONVISIBLE_REQUEST_DISCARD);
|
||||
Some(OnNonvisible::DISCARD_IMAGES));
|
||||
|
||||
if (mNewRequestsWillNeedAnimationReset) {
|
||||
mCurrentRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET;
|
||||
|
@ -1274,7 +1244,7 @@ nsImageLoadingContent::PreparePendingRequest(ImageLoadType aImageLoadType)
|
|||
{
|
||||
// Get rid of anything that was there previously.
|
||||
ClearPendingRequest(NS_ERROR_IMAGE_SRC_CHANGED,
|
||||
ON_NONVISIBLE_REQUEST_DISCARD);
|
||||
Some(OnNonvisible::DISCARD_IMAGES));
|
||||
|
||||
if (mNewRequestsWillNeedAnimationReset) {
|
||||
mPendingRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET;
|
||||
|
@ -1339,7 +1309,7 @@ nsImageLoadingContent::MakePendingRequestCurrent()
|
|||
|
||||
void
|
||||
nsImageLoadingContent::ClearCurrentRequest(nsresult aReason,
|
||||
uint32_t aNonvisibleAction)
|
||||
const Maybe<OnNonvisible>& aNonvisibleAction)
|
||||
{
|
||||
if (!mCurrentRequest) {
|
||||
// Even if we didn't have a current request, we might have been keeping
|
||||
|
@ -1365,7 +1335,7 @@ nsImageLoadingContent::ClearCurrentRequest(nsresult aReason,
|
|||
|
||||
void
|
||||
nsImageLoadingContent::ClearPendingRequest(nsresult aReason,
|
||||
uint32_t aNonvisibleAction)
|
||||
const Maybe<OnNonvisible>& aNonvisibleAction)
|
||||
{
|
||||
if (!mPendingRequest)
|
||||
return;
|
||||
|
@ -1451,6 +1421,27 @@ nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
doc->UnblockOnload(false);
|
||||
}
|
||||
|
||||
void
|
||||
nsImageLoadingContent::OnVisibilityChange(Visibility aNewVisibility,
|
||||
const Maybe<OnNonvisible>& aNonvisibleAction)
|
||||
{
|
||||
switch (aNewVisibility) {
|
||||
case Visibility::APPROXIMATELY_VISIBLE:
|
||||
TrackImage(mCurrentRequest);
|
||||
TrackImage(mPendingRequest);
|
||||
break;
|
||||
|
||||
case Visibility::APPROXIMATELY_NONVISIBLE:
|
||||
UntrackImage(mCurrentRequest, aNonvisibleAction);
|
||||
UntrackImage(mPendingRequest, aNonvisibleAction);
|
||||
break;
|
||||
|
||||
case Visibility::UNTRACKED:
|
||||
MOZ_ASSERT_UNREACHABLE("Shouldn't notify for untracked visibility");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsImageLoadingContent::TrackImage(imgIRequest* aImage)
|
||||
{
|
||||
|
@ -1461,32 +1452,34 @@ nsImageLoadingContent::TrackImage(imgIRequest* aImage)
|
|||
"Why haven't we heard of this request?");
|
||||
|
||||
nsIDocument* doc = GetOurCurrentDoc();
|
||||
if (doc && (mFrameCreateCalled || GetOurPrimaryFrame()) &&
|
||||
(mVisibleCount > 0)) {
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mVisibleCount == 1) {
|
||||
// Since we're becoming visible, request a decode.
|
||||
nsImageFrame* f = do_QueryFrame(GetOurPrimaryFrame());
|
||||
if (f) {
|
||||
f->MaybeDecodeForPredictedSize();
|
||||
}
|
||||
}
|
||||
// We only want to track this request if we're visible. Ordinarily we check
|
||||
// the visible count, but that requires a frame; in cases where
|
||||
// GetOurPrimaryFrame() cannot obtain a frame (e.g. <feImage>), we assume
|
||||
// we're visible if FrameCreated() was called.
|
||||
nsIFrame* frame = GetOurPrimaryFrame();
|
||||
if ((frame && frame->GetVisibility() == Visibility::APPROXIMATELY_NONVISIBLE) ||
|
||||
(!frame && !mFrameCreateCalled)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mCurrentRequestFlags |= REQUEST_IS_TRACKED;
|
||||
doc->AddImage(mCurrentRequest);
|
||||
}
|
||||
if (aImage == mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mPendingRequestFlags |= REQUEST_IS_TRACKED;
|
||||
doc->AddImage(mPendingRequest);
|
||||
}
|
||||
if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mCurrentRequestFlags |= REQUEST_IS_TRACKED;
|
||||
doc->AddImage(mCurrentRequest);
|
||||
}
|
||||
if (aImage == mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mPendingRequestFlags |= REQUEST_IS_TRACKED;
|
||||
doc->AddImage(mPendingRequest);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsImageLoadingContent::UntrackImage(imgIRequest* aImage,
|
||||
uint32_t aNonvisibleAction
|
||||
/* = ON_NONVISIBLE_NO_ACTION */)
|
||||
const Maybe<OnNonvisible>& aNonvisibleAction
|
||||
/* = Nothing() */)
|
||||
{
|
||||
if (!aImage)
|
||||
return;
|
||||
|
@ -1503,10 +1496,10 @@ nsImageLoadingContent::UntrackImage(imgIRequest* aImage,
|
|||
if (doc && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mCurrentRequestFlags &= ~REQUEST_IS_TRACKED;
|
||||
doc->RemoveImage(mCurrentRequest,
|
||||
(aNonvisibleAction == ON_NONVISIBLE_REQUEST_DISCARD)
|
||||
aNonvisibleAction == Some(OnNonvisible::DISCARD_IMAGES)
|
||||
? nsIDocument::REQUEST_DISCARD
|
||||
: 0);
|
||||
} else if (aNonvisibleAction == ON_NONVISIBLE_REQUEST_DISCARD) {
|
||||
} else if (aNonvisibleAction == Some(OnNonvisible::DISCARD_IMAGES)) {
|
||||
// If we're not in the document we may still need to be discarded.
|
||||
aImage->RequestDiscard();
|
||||
}
|
||||
|
@ -1515,10 +1508,10 @@ nsImageLoadingContent::UntrackImage(imgIRequest* aImage,
|
|||
if (doc && (mPendingRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mPendingRequestFlags &= ~REQUEST_IS_TRACKED;
|
||||
doc->RemoveImage(mPendingRequest,
|
||||
(aNonvisibleAction == ON_NONVISIBLE_REQUEST_DISCARD)
|
||||
aNonvisibleAction == Some(OnNonvisible::DISCARD_IMAGES)
|
||||
? nsIDocument::REQUEST_DISCARD
|
||||
: 0);
|
||||
} else if (aNonvisibleAction == ON_NONVISIBLE_REQUEST_DISCARD) {
|
||||
} else if (aNonvisibleAction == Some(OnNonvisible::DISCARD_IMAGES)) {
|
||||
// If we're not in the document we may still need to be discarded.
|
||||
aImage->RequestDiscard();
|
||||
}
|
||||
|
|
|
@ -41,6 +41,11 @@ class imgRequestProxy;
|
|||
class nsImageLoadingContent : public nsIImageLoadingContent,
|
||||
public imgIOnloadBlocker
|
||||
{
|
||||
template <typename T> using Maybe = mozilla::Maybe<T>;
|
||||
using Nothing = mozilla::Nothing;
|
||||
using OnNonvisible = mozilla::OnNonvisible;
|
||||
using Visibility = mozilla::Visibility;
|
||||
|
||||
/* METHODS */
|
||||
public:
|
||||
nsImageLoadingContent();
|
||||
|
@ -318,8 +323,10 @@ protected:
|
|||
* @param aNonvisibleAction An action to take if the image is no longer
|
||||
* visible as a result; see |UntrackImage|.
|
||||
*/
|
||||
void ClearCurrentRequest(nsresult aReason, uint32_t aNonvisibleAction);
|
||||
void ClearPendingRequest(nsresult aReason, uint32_t aNonvisibleAction);
|
||||
void ClearCurrentRequest(nsresult aReason,
|
||||
const Maybe<OnNonvisible>& aNonvisibleAction = Nothing());
|
||||
void ClearPendingRequest(nsresult aReason,
|
||||
const Maybe<OnNonvisible>& aNonvisibleAction = Nothing());
|
||||
|
||||
/**
|
||||
* Retrieve a pointer to the 'registered with the refresh driver' flag for
|
||||
|
@ -348,14 +355,16 @@ protected:
|
|||
*
|
||||
* No-op if aImage is null.
|
||||
*
|
||||
* @param aNonvisibleAction What to do if the image's visibility count is now
|
||||
* zero. If ON_NONVISIBLE_NO_ACTION, nothing will be
|
||||
* done. If ON_NONVISIBLE_REQUEST_DISCARD, the image
|
||||
* will be asked to discard its surfaces if possible.
|
||||
* @param aNonvisibleAction A requested action if the frame has become
|
||||
* nonvisible. If Nothing(), no action is
|
||||
* requested. If DISCARD_IMAGES is specified, the
|
||||
* frame is requested to ask any images it's
|
||||
* associated with to discard their surfaces if
|
||||
* possible.
|
||||
*/
|
||||
void TrackImage(imgIRequest* aImage);
|
||||
void UntrackImage(imgIRequest* aImage,
|
||||
uint32_t aNonvisibleAction = ON_NONVISIBLE_NO_ACTION);
|
||||
const Maybe<OnNonvisible>& aNonvisibleAction = Nothing());
|
||||
|
||||
/* MEMBERS */
|
||||
RefPtr<imgRequestProxy> mCurrentRequest;
|
||||
|
@ -439,8 +448,6 @@ private:
|
|||
|
||||
// True when FrameCreate has been called but FrameDestroy has not.
|
||||
bool mFrameCreateCalled;
|
||||
|
||||
uint32_t mVisibleCount;
|
||||
};
|
||||
|
||||
#endif // nsImageLoadingContent_h__
|
||||
|
|
|
@ -225,7 +225,7 @@ public:
|
|||
PAINTING,
|
||||
EVENT_DELIVERY,
|
||||
PLUGIN_GEOMETRY,
|
||||
IMAGE_VISIBILITY,
|
||||
FRAME_VISIBILITY,
|
||||
TRANSFORM_COMPUTATION
|
||||
};
|
||||
nsDisplayListBuilder(nsIFrame* aReferenceFrame, Mode aMode, bool aBuildCaret);
|
||||
|
@ -261,11 +261,13 @@ public:
|
|||
* @return true if the display list is being built for painting.
|
||||
*/
|
||||
bool IsForPainting() { return mMode == PAINTING; }
|
||||
|
||||
/**
|
||||
* @return true if the display list is being built for determining image
|
||||
* @return true if the display list is being built for determining frame
|
||||
* visibility.
|
||||
*/
|
||||
bool IsForImageVisibility() { return mMode == IMAGE_VISIBILITY; }
|
||||
bool IsForFrameVisibility() { return mMode == FRAME_VISIBILITY; }
|
||||
|
||||
bool WillComputePluginGeometry() { return mWillComputePluginGeometry; }
|
||||
/**
|
||||
* @return true if "painting is suppressed" during page load and we
|
||||
|
|
|
@ -138,10 +138,10 @@ typedef struct CapturingContentInfo {
|
|||
mozilla::StaticRefPtr<nsIContent> mContent;
|
||||
} CapturingContentInfo;
|
||||
|
||||
// f17842ee-f1f0-4193-814f-70d706b67060
|
||||
// a75573d6-34c8-4485-8fb7-edcb6fc70e12
|
||||
#define NS_IPRESSHELL_IID \
|
||||
{ 0xf17842ee, 0xf1f0, 0x4193, \
|
||||
{ 0x81, 0x4f, 0x70, 0xd7, 0x06, 0xb6, 0x70, 0x60 } }
|
||||
{ 0xa75573d6, 0x34c8, 0x4485, \
|
||||
{ 0x8f, 0xb7, 0xed, 0xcb, 0x6f, 0xc7, 0x0e, 0x12 } }
|
||||
|
||||
// debug VerifyReflow flags
|
||||
#define VERIFY_REFLOW_ON 0x01
|
||||
|
@ -1564,23 +1564,38 @@ public:
|
|||
void InvalidatePresShellIfHidden();
|
||||
void CancelInvalidatePresShellIfHidden();
|
||||
|
||||
// Schedule an update of the list of visible images.
|
||||
virtual void ScheduleImageVisibilityUpdate() = 0;
|
||||
|
||||
// Clears the current list of visible images on this presshell and replaces it
|
||||
// with images that are in the display list aList.
|
||||
virtual void RebuildImageVisibilityDisplayList(const nsDisplayList& aList) = 0;
|
||||
virtual void RebuildImageVisibility(nsRect* aRect = nullptr,
|
||||
bool aRemoveOnly = false) = 0;
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Approximate frame visibility tracking public API.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Ensures the image is in the list of visible images.
|
||||
virtual void EnsureImageInVisibleList(nsIImageLoadingContent* aImage) = 0;
|
||||
/// Schedule an update of the list of approximately visible frames "soon".
|
||||
/// This lets the refresh driver know that we want a visibility update in the
|
||||
/// near future. The refresh driver applies its own heuristics and throttling
|
||||
/// to decide when to actually perform the visibility update.
|
||||
virtual void ScheduleApproximateFrameVisibilityUpdateSoon() = 0;
|
||||
|
||||
// Removes the image from the list of visible images if it is present there.
|
||||
virtual void RemoveImageFromVisibleList(nsIImageLoadingContent* aImage) = 0;
|
||||
/// Schedule an update of the list of approximately visible frames "now". The
|
||||
/// update runs asynchronously, but it will be posted to the event loop
|
||||
/// immediately. Prefer the "soon" variation of this method when possible, as
|
||||
/// this variation ignores the refresh driver's heuristics.
|
||||
virtual void ScheduleApproximateFrameVisibilityUpdateNow() = 0;
|
||||
|
||||
/// Clears the current list of approximately visible frames on this pres shell
|
||||
/// and replaces it with frames that are in the display list @aList.
|
||||
virtual void RebuildApproximateFrameVisibilityDisplayList(const nsDisplayList& aList) = 0;
|
||||
virtual void RebuildApproximateFrameVisibility(nsRect* aRect = nullptr,
|
||||
bool aRemoveOnly = false) = 0;
|
||||
|
||||
/// Ensures @aFrame is in the list of approximately visible frames.
|
||||
virtual void EnsureFrameInApproximatelyVisibleList(nsIFrame* aFrame) = 0;
|
||||
|
||||
/// Removes @aFrame from the list of approximately visible frames if present.
|
||||
virtual void RemoveFrameFromApproximatelyVisibleList(nsIFrame* aFrame) = 0;
|
||||
|
||||
/// Whether we should assume all frames are visible.
|
||||
virtual bool AssumeAllFramesVisible() = 0;
|
||||
|
||||
// Whether we should assume all images are visible.
|
||||
virtual bool AssumeAllImagesVisible() = 0;
|
||||
|
||||
/**
|
||||
* Returns whether the document's style set's rule processor for the
|
||||
|
|
|
@ -1214,35 +1214,35 @@ nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent,
|
|||
|
||||
scrollableFrame->TriggerDisplayPortExpiration();
|
||||
|
||||
// Display port margins changing means that the set of visible images may
|
||||
// Display port margins changing means that the set of visible frames may
|
||||
// have drastically changed. Check if we should schedule an update.
|
||||
hadDisplayPort =
|
||||
scrollableFrame->GetDisplayPortAtLastImageVisibilityUpdate(&oldDisplayPort);
|
||||
scrollableFrame->GetDisplayPortAtLastApproximateFrameVisibilityUpdate(&oldDisplayPort);
|
||||
|
||||
bool needImageVisibilityUpdate = !hadDisplayPort;
|
||||
bool needVisibilityUpdate = !hadDisplayPort;
|
||||
// Check if the total size has changed by a large factor.
|
||||
if (!needImageVisibilityUpdate) {
|
||||
if (!needVisibilityUpdate) {
|
||||
if ((newDisplayPort.width > 2 * oldDisplayPort.width) ||
|
||||
(oldDisplayPort.width > 2 * newDisplayPort.width) ||
|
||||
(newDisplayPort.height > 2 * oldDisplayPort.height) ||
|
||||
(oldDisplayPort.height > 2 * newDisplayPort.height)) {
|
||||
needImageVisibilityUpdate = true;
|
||||
needVisibilityUpdate = true;
|
||||
}
|
||||
}
|
||||
// Check if it's moved by a significant amount.
|
||||
if (!needImageVisibilityUpdate) {
|
||||
if (!needVisibilityUpdate) {
|
||||
if (nsRect* baseData = static_cast<nsRect*>(aContent->GetProperty(nsGkAtoms::DisplayPortBase))) {
|
||||
nsRect base = *baseData;
|
||||
if ((std::abs(newDisplayPort.X() - oldDisplayPort.X()) > base.width) ||
|
||||
(std::abs(newDisplayPort.XMost() - oldDisplayPort.XMost()) > base.width) ||
|
||||
(std::abs(newDisplayPort.Y() - oldDisplayPort.Y()) > base.height) ||
|
||||
(std::abs(newDisplayPort.YMost() - oldDisplayPort.YMost()) > base.height)) {
|
||||
needImageVisibilityUpdate = true;
|
||||
needVisibilityUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (needImageVisibilityUpdate) {
|
||||
aPresShell->ScheduleImageVisibilityUpdate();
|
||||
if (needVisibilityUpdate) {
|
||||
aPresShell->ScheduleApproximateFrameVisibilityUpdateNow();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -8054,77 +8054,6 @@ nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame* aFrame,
|
|||
return shadows;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsLayoutUtils::UpdateImageVisibilityForFrame(nsIFrame* aImageFrame)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
nsIAtom* type = aImageFrame->GetType();
|
||||
MOZ_ASSERT(type == nsGkAtoms::imageFrame ||
|
||||
type == nsGkAtoms::imageControlFrame ||
|
||||
type == nsGkAtoms::svgImageFrame, "wrong type of frame");
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIImageLoadingContent> content = do_QueryInterface(aImageFrame->GetContent());
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIPresShell* presShell = aImageFrame->PresContext()->PresShell();
|
||||
if (presShell->AssumeAllImagesVisible()) {
|
||||
presShell->EnsureImageInVisibleList(content);
|
||||
return;
|
||||
}
|
||||
|
||||
bool visible = true;
|
||||
nsIFrame* f = aImageFrame->GetParent();
|
||||
nsRect rect = aImageFrame->GetContentRectRelativeToSelf();
|
||||
nsIFrame* rectFrame = aImageFrame;
|
||||
while (f) {
|
||||
nsIScrollableFrame* sf = do_QueryFrame(f);
|
||||
if (sf) {
|
||||
nsRect transformedRect =
|
||||
nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f);
|
||||
if (!sf->IsRectNearlyVisible(transformedRect)) {
|
||||
visible = false;
|
||||
break;
|
||||
}
|
||||
// Move transformedRect to be contained in the scrollport as best we can
|
||||
// (it might not fit) to pretend that it was scrolled into view.
|
||||
nsRect scrollPort = sf->GetScrollPortRect();
|
||||
if (transformedRect.XMost() > scrollPort.XMost()) {
|
||||
transformedRect.x -= transformedRect.XMost() - scrollPort.XMost();
|
||||
}
|
||||
if (transformedRect.x < scrollPort.x) {
|
||||
transformedRect.x = scrollPort.x;
|
||||
}
|
||||
if (transformedRect.YMost() > scrollPort.YMost()) {
|
||||
transformedRect.y -= transformedRect.YMost() - scrollPort.YMost();
|
||||
}
|
||||
if (transformedRect.y < scrollPort.y) {
|
||||
transformedRect.y = scrollPort.y;
|
||||
}
|
||||
transformedRect.width = std::min(transformedRect.width, scrollPort.width);
|
||||
transformedRect.height = std::min(transformedRect.height, scrollPort.height);
|
||||
rect = transformedRect;
|
||||
rectFrame = f;
|
||||
}
|
||||
nsIFrame* parent = f->GetParent();
|
||||
if (!parent) {
|
||||
parent = nsLayoutUtils::GetCrossDocParentFrame(f);
|
||||
if (parent && parent->PresContext()->IsChrome()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
f = parent;
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
presShell->EnsureImageInVisibleList(content);
|
||||
} else {
|
||||
presShell->RemoveImageFromVisibleList(content);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsLayoutUtils::GetContentViewerSize(nsPresContext* aPresContext,
|
||||
LayoutDeviceIntSize& aOutSize)
|
||||
|
|
|
@ -2543,14 +2543,6 @@ public:
|
|||
nsRegion* aPreciseTargetDest,
|
||||
nsRegion* aImpreciseTargetDest);
|
||||
|
||||
/**
|
||||
* Determine if aImageFrame (which is an nsImageFrame, nsImageControlFrame, or
|
||||
* nsSVGImageFrame) is visible or close to being visible via scrolling and
|
||||
* update the presshell with this knowledge.
|
||||
*/
|
||||
static void
|
||||
UpdateImageVisibilityForFrame(nsIFrame* aImageFrame);
|
||||
|
||||
/**
|
||||
* Populate aOutSize with the size of the content viewer corresponding
|
||||
* to the given prescontext. Return true if the size was set, false
|
||||
|
|
|
@ -1170,9 +1170,9 @@ PresShell::Destroy()
|
|||
|
||||
mSynthMouseMoveEvent.Revoke();
|
||||
|
||||
mUpdateImageVisibilityEvent.Revoke();
|
||||
mUpdateApproximateFrameVisibilityEvent.Revoke();
|
||||
|
||||
ClearVisibleImagesList(nsIImageLoadingContent::ON_NONVISIBLE_REQUEST_DISCARD);
|
||||
ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DISCARD_IMAGES));
|
||||
|
||||
if (mCaret) {
|
||||
mCaret->Terminate();
|
||||
|
@ -3776,7 +3776,7 @@ PresShell::UnsuppressAndInvalidate()
|
|||
|
||||
if (!mHaveShutDown) {
|
||||
SynthesizeMouseMove(false);
|
||||
ScheduleImageVisibilityUpdate();
|
||||
ScheduleApproximateFrameVisibilityUpdateNow();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5583,87 +5583,73 @@ AddFrameToVisibleRegions(nsIFrame* aFrame,
|
|||
}
|
||||
|
||||
/* static */ void
|
||||
PresShell::MarkImagesInListVisible(const nsDisplayList& aList,
|
||||
Maybe<VisibleRegions>& aVisibleRegions)
|
||||
PresShell::MarkFramesInListApproximatelyVisible(const nsDisplayList& aList,
|
||||
Maybe<VisibleRegions>& aVisibleRegions)
|
||||
{
|
||||
for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
|
||||
nsDisplayList* sublist = item->GetChildren();
|
||||
if (sublist) {
|
||||
MarkImagesInListVisible(*sublist, aVisibleRegions);
|
||||
MarkFramesInListApproximatelyVisible(*sublist, aVisibleRegions);
|
||||
continue;
|
||||
}
|
||||
nsIFrame* f = item->Frame();
|
||||
// We could check the type of the display item, only a handful can hold an
|
||||
// image loading content.
|
||||
// dont bother nscomptr here, it is wasteful
|
||||
nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(f->GetContent()));
|
||||
if (content) {
|
||||
// use the presshell containing the image
|
||||
PresShell* presShell = static_cast<PresShell*>(f->PresContext()->PresShell());
|
||||
uint32_t count = presShell->mVisibleImages.Count();
|
||||
presShell->mVisibleImages.PutEntry(content);
|
||||
if (presShell->mVisibleImages.Count() > count) {
|
||||
// content was added to mVisibleImages, so we need to increment its visible count
|
||||
content->IncrementVisibleCount();
|
||||
}
|
||||
|
||||
AddFrameToVisibleRegions(f, presShell->mViewManager, aVisibleRegions);
|
||||
nsIFrame* frame = item->Frame();
|
||||
MOZ_ASSERT(frame);
|
||||
|
||||
if (!frame->TrackingVisibility()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use the presshell containing the frame.
|
||||
auto* presShell = static_cast<PresShell*>(frame->PresContext()->PresShell());
|
||||
uint32_t count = presShell->mApproximatelyVisibleFrames.Count();
|
||||
MOZ_ASSERT(!presShell->AssumeAllFramesVisible());
|
||||
presShell->mApproximatelyVisibleFrames.PutEntry(frame);
|
||||
if (presShell->mApproximatelyVisibleFrames.Count() > count) {
|
||||
// The frame was added to mApproximatelyVisibleFrames, so increment its visible count.
|
||||
frame->IncApproximateVisibleCount();
|
||||
}
|
||||
|
||||
AddFrameToVisibleRegions(frame, presShell->mViewManager, aVisibleRegions);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::ReportAnyBadState()
|
||||
PresShell::ReportBadStateDuringVisibilityUpdate()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
gfxCriticalNote << "Got null image in image visibility: off-main-thread";
|
||||
gfxCriticalNote << "Got null frame in frame visibility: off-main-thread";
|
||||
}
|
||||
if (mIsZombie) {
|
||||
gfxCriticalNote << "Got null image in image visibility: mIsZombie";
|
||||
gfxCriticalNote << "Got null frame in frame visibility: mIsZombie";
|
||||
}
|
||||
if (mIsDestroying) {
|
||||
gfxCriticalNote << "Got null image in image visibility: mIsDestroying";
|
||||
gfxCriticalNote << "Got null frame in frame visibility: mIsDestroying";
|
||||
}
|
||||
if (mIsReflowing) {
|
||||
gfxCriticalNote << "Got null image in image visibility: mIsReflowing";
|
||||
gfxCriticalNote << "Got null frame in frame visibility: mIsReflowing";
|
||||
}
|
||||
if (mPaintingIsFrozen) {
|
||||
gfxCriticalNote << "Got null image in image visibility: mPaintingIsFrozen";
|
||||
gfxCriticalNote << "Got null frame in frame visibility: mPaintingIsFrozen";
|
||||
}
|
||||
if (mForwardingContainer) {
|
||||
gfxCriticalNote << "Got null image in image visibility: mForwardingContainer";
|
||||
gfxCriticalNote << "Got null frame in frame visibility: mForwardingContainer";
|
||||
}
|
||||
if (mIsNeverPainting) {
|
||||
gfxCriticalNote << "Got null image in image visibility: mIsNeverPainting";
|
||||
gfxCriticalNote << "Got null frame in frame visibility: mIsNeverPainting";
|
||||
}
|
||||
if (mIsDocumentGone) {
|
||||
gfxCriticalNote << "Got null image in image visibility: mIsDocumentGone";
|
||||
gfxCriticalNote << "Got null frame in frame visibility: mIsDocumentGone";
|
||||
}
|
||||
if (!nsContentUtils::IsSafeToRunScript()) {
|
||||
gfxCriticalNote << "Got null image in image visibility: not safe to run script";
|
||||
gfxCriticalNote << "Got null frame in frame visibility: not safe to run script";
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::SetInImageVisibility(bool aState)
|
||||
PresShell::SetInFrameVisibilityUpdate(bool aState)
|
||||
{
|
||||
mInImageVisibility = aState;
|
||||
}
|
||||
|
||||
static void
|
||||
DecrementVisibleCount(nsTHashtable<nsRefPtrHashKey<nsIImageLoadingContent>>& aImages,
|
||||
uint32_t aNonvisibleAction, PresShell* aPresShell)
|
||||
{
|
||||
for (auto iter = aImages.Iter(); !iter.Done(); iter.Next()) {
|
||||
if (MOZ_UNLIKELY(!iter.Get()->GetKey())) {
|
||||
// We are about to crash, annotate crash report with some info that might
|
||||
// help debug the crash (bug 1251150)
|
||||
aPresShell->ReportAnyBadState();
|
||||
}
|
||||
aPresShell->SetInImageVisibility(true);
|
||||
iter.Get()->GetKey()->DecrementVisibleCount(aNonvisibleAction);
|
||||
aPresShell->SetInImageVisibility(false);
|
||||
}
|
||||
mInFrameVisibilityUpdate = aState;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -5716,15 +5702,43 @@ NotifyCompositorOfVisibleRegionsChange(PresShell* aPresShell,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::RebuildImageVisibilityDisplayList(const nsDisplayList& aList)
|
||||
/* static */ void
|
||||
PresShell::DecApproximateVisibleCount(VisibleFrames& aFrames,
|
||||
Maybe<OnNonvisible> aNonvisibleAction
|
||||
/* = Nothing() */)
|
||||
{
|
||||
MOZ_ASSERT(!mImageVisibilityVisited, "already visited?");
|
||||
mImageVisibilityVisited = true;
|
||||
// Remove the entries of the mVisibleImages hashtable and put them in
|
||||
// oldVisibleImages.
|
||||
nsTHashtable< nsRefPtrHashKey<nsIImageLoadingContent> > oldVisibleImages;
|
||||
mVisibleImages.SwapElements(oldVisibleImages);
|
||||
for (auto iter = aFrames.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsIFrame* frame = iter.Get()->GetKey();
|
||||
|
||||
if (MOZ_UNLIKELY(!frame)) {
|
||||
// We are about to crash, annotate crash report with some info that might
|
||||
// help debug the crash (bug 1251150)
|
||||
ReportBadStateDuringVisibilityUpdate();
|
||||
}
|
||||
|
||||
SetInFrameVisibilityUpdate(true);
|
||||
|
||||
// Decrement the frame's visible count if we're still tracking its
|
||||
// visibility. (We may not be, if the frame disabled visibility tracking
|
||||
// after we added it to the visible frames list.)
|
||||
if (frame->TrackingVisibility()) {
|
||||
frame->DecApproximateVisibleCount(aNonvisibleAction);
|
||||
}
|
||||
|
||||
SetInFrameVisibilityUpdate(false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::RebuildApproximateFrameVisibilityDisplayList(const nsDisplayList& aList)
|
||||
{
|
||||
MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "already visited?");
|
||||
mApproximateFrameVisibilityVisited = true;
|
||||
|
||||
// Remove the entries of the mApproximatelyVisibleFrames hashtable and put
|
||||
// them in oldApproxVisibleFrames.
|
||||
VisibleFrames oldApproximatelyVisibleFrames;
|
||||
mApproximatelyVisibleFrames.SwapElements(oldApproximatelyVisibleFrames);
|
||||
|
||||
// If we're visualizing visible regions, create a VisibleRegions object to
|
||||
// store information about them. The functions we call will populate this
|
||||
|
@ -5735,58 +5749,58 @@ PresShell::RebuildImageVisibilityDisplayList(const nsDisplayList& aList)
|
|||
visibleRegions.emplace();
|
||||
}
|
||||
|
||||
MarkImagesInListVisible(aList, visibleRegions);
|
||||
MarkFramesInListApproximatelyVisible(aList, visibleRegions);
|
||||
|
||||
DecrementVisibleCount(oldVisibleImages,
|
||||
nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION, this);
|
||||
DecApproximateVisibleCount(oldApproximatelyVisibleFrames);
|
||||
|
||||
NotifyCompositorOfVisibleRegionsChange(this, visibleRegions);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PresShell::ClearImageVisibilityVisited(nsView* aView, bool aClear)
|
||||
PresShell::ClearApproximateFrameVisibilityVisited(nsView* aView, bool aClear)
|
||||
{
|
||||
nsViewManager* vm = aView->GetViewManager();
|
||||
if (aClear) {
|
||||
PresShell* presShell = static_cast<PresShell*>(vm->GetPresShell());
|
||||
if (!presShell->mImageVisibilityVisited) {
|
||||
presShell->ClearVisibleImagesList(
|
||||
nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION);
|
||||
if (!presShell->mApproximateFrameVisibilityVisited) {
|
||||
presShell->ClearApproximatelyVisibleFramesList();
|
||||
}
|
||||
presShell->mImageVisibilityVisited = false;
|
||||
presShell->mApproximateFrameVisibilityVisited = false;
|
||||
}
|
||||
for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
|
||||
ClearImageVisibilityVisited(v, v->GetViewManager() != vm);
|
||||
ClearApproximateFrameVisibilityVisited(v, v->GetViewManager() != vm);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::ClearVisibleImagesList(uint32_t aNonvisibleAction)
|
||||
PresShell::ClearApproximatelyVisibleFramesList(Maybe<OnNonvisible> aNonvisibleAction
|
||||
/* = Nothing() */)
|
||||
{
|
||||
if (mInImageVisibility) {
|
||||
gfxCriticalNoteOnce << "ClearVisibleImagesList is re-entering on "
|
||||
if (mInFrameVisibilityUpdate) {
|
||||
gfxCriticalNoteOnce << "ClearApproximatelyVisibleFramesList is re-entering on "
|
||||
<< (NS_IsMainThread() ? "" : "non-") << "main thread";
|
||||
}
|
||||
DecrementVisibleCount(mVisibleImages, aNonvisibleAction, this);
|
||||
mVisibleImages.Clear();
|
||||
DecApproximateVisibleCount(mApproximatelyVisibleFrames, aNonvisibleAction);
|
||||
mApproximatelyVisibleFrames.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame,
|
||||
const nsRect& aRect,
|
||||
Maybe<VisibleRegions>& aVisibleRegions,
|
||||
bool aRemoveOnly /* = false */)
|
||||
PresShell::MarkFramesInSubtreeApproximatelyVisible(nsIFrame* aFrame,
|
||||
const nsRect& aRect,
|
||||
Maybe<VisibleRegions>& aVisibleRegions,
|
||||
bool aRemoveOnly /* = false */)
|
||||
{
|
||||
MOZ_ASSERT(aFrame->PresContext()->PresShell() == this, "wrong presshell");
|
||||
|
||||
nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(aFrame->GetContent()));
|
||||
if (content && aFrame->StyleVisibility()->IsVisible() &&
|
||||
(!aRemoveOnly || content->GetVisibleCount() > 0)) {
|
||||
uint32_t count = mVisibleImages.Count();
|
||||
mVisibleImages.PutEntry(content);
|
||||
if (mVisibleImages.Count() > count) {
|
||||
// content was added to mVisibleImages, so we need to increment its visible count
|
||||
content->IncrementVisibleCount();
|
||||
if (aFrame->TrackingVisibility() &&
|
||||
aFrame->StyleVisibility()->IsVisible() &&
|
||||
(!aRemoveOnly || aFrame->GetVisibility() == Visibility::APPROXIMATELY_VISIBLE)) {
|
||||
MOZ_ASSERT(!AssumeAllFramesVisible());
|
||||
uint32_t count = mApproximatelyVisibleFrames.Count();
|
||||
mApproximatelyVisibleFrames.PutEntry(aFrame);
|
||||
if (mApproximatelyVisibleFrames.Count() > count) {
|
||||
// The frame was added to mApproximatelyVisibleFrames, so increment its visible count.
|
||||
aFrame->IncApproximateVisibleCount();
|
||||
}
|
||||
|
||||
AddFrameToVisibleRegions(aFrame, mViewManager, aVisibleRegions);
|
||||
|
@ -5796,7 +5810,7 @@ PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame,
|
|||
if (subdocFrame) {
|
||||
nsIPresShell* presShell = subdocFrame->GetSubdocumentPresShellForPainting(
|
||||
nsSubDocumentFrame::IGNORE_PAINT_SUPPRESSION);
|
||||
if (presShell) {
|
||||
if (presShell && !presShell->AssumeAllFramesVisible()) {
|
||||
nsRect rect = aRect;
|
||||
nsIFrame* root = presShell->GetRootFrame();
|
||||
if (root) {
|
||||
|
@ -5808,7 +5822,7 @@ PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame,
|
|||
aFrame->PresContext()->AppUnitsPerDevPixel(),
|
||||
presShell->GetPresContext()->AppUnitsPerDevPixel());
|
||||
|
||||
presShell->RebuildImageVisibility(&rect);
|
||||
presShell->RebuildApproximateFrameVisibility(&rect);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -5817,7 +5831,7 @@ PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame,
|
|||
|
||||
nsIScrollableFrame* scrollFrame = do_QueryFrame(aFrame);
|
||||
if (scrollFrame) {
|
||||
scrollFrame->NotifyImageVisibilityUpdate();
|
||||
scrollFrame->NotifyApproximateFrameVisibilityUpdate();
|
||||
nsRect displayPort;
|
||||
bool usingDisplayport =
|
||||
nsLayoutUtils::GetDisplayPortForVisibilityTesting(
|
||||
|
@ -5832,7 +5846,7 @@ PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame,
|
|||
|
||||
bool preserves3DChildren = aFrame->Extend3DContext();
|
||||
|
||||
// we assume all images in popups are visible elsewhere, so we skip them here
|
||||
// We assume all frames in popups are visible, so we skip them here.
|
||||
const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
|
||||
nsIFrame::kSelectPopupList);
|
||||
for (nsIFrame::ChildListIterator childLists(aFrame);
|
||||
|
@ -5858,32 +5872,32 @@ PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame,
|
|||
}
|
||||
}
|
||||
}
|
||||
MarkImagesInSubtreeVisible(child, r, aVisibleRegions);
|
||||
MarkFramesInSubtreeApproximatelyVisible(child, r, aVisibleRegions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::RebuildImageVisibility(nsRect* aRect,
|
||||
bool aRemoveOnly /* = false */)
|
||||
PresShell::RebuildApproximateFrameVisibility(nsRect* aRect,
|
||||
bool aRemoveOnly /* = false */)
|
||||
{
|
||||
MOZ_ASSERT(!mImageVisibilityVisited, "already visited?");
|
||||
mImageVisibilityVisited = true;
|
||||
MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "already visited?");
|
||||
mApproximateFrameVisibilityVisited = true;
|
||||
|
||||
nsIFrame* rootFrame = GetRootFrame();
|
||||
if (!rootFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mInImageVisibility) {
|
||||
gfxCriticalNoteOnce << "RebuildImageVisibility is re-entering on "
|
||||
if (mInFrameVisibilityUpdate) {
|
||||
gfxCriticalNoteOnce << "RebuildApproximateFrameVisibility is re-entering on "
|
||||
<< (NS_IsMainThread() ? "" : "non-") << "main thread";
|
||||
}
|
||||
|
||||
// Remove the entries of the mVisibleImages hashtable and put them in
|
||||
// oldVisibleImages.
|
||||
nsTHashtable< nsRefPtrHashKey<nsIImageLoadingContent> > oldVisibleImages;
|
||||
mVisibleImages.SwapElements(oldVisibleImages);
|
||||
// Remove the entries of the mApproximatelyVisibleFrames hashtable and put
|
||||
// them in oldApproximatelyVisibleFrames.
|
||||
VisibleFrames oldApproximatelyVisibleFrames;
|
||||
mApproximatelyVisibleFrames.SwapElements(oldApproximatelyVisibleFrames);
|
||||
|
||||
// If we're visualizing visible regions, create a VisibleRegions object to
|
||||
// store information about them. The functions we call will populate this
|
||||
|
@ -5899,27 +5913,26 @@ PresShell::RebuildImageVisibility(nsRect* aRect,
|
|||
vis = *aRect;
|
||||
}
|
||||
|
||||
MarkImagesInSubtreeVisible(rootFrame, vis, visibleRegions, aRemoveOnly);
|
||||
MarkFramesInSubtreeApproximatelyVisible(rootFrame, vis, visibleRegions, aRemoveOnly);
|
||||
|
||||
DecrementVisibleCount(oldVisibleImages,
|
||||
nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION, this);
|
||||
DecApproximateVisibleCount(oldApproximatelyVisibleFrames);
|
||||
|
||||
NotifyCompositorOfVisibleRegionsChange(this, visibleRegions);
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::UpdateImageVisibility()
|
||||
PresShell::UpdateApproximateFrameVisibility()
|
||||
{
|
||||
DoUpdateImageVisibility(/* aRemoveOnly = */ false);
|
||||
DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ false);
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::DoUpdateImageVisibility(bool aRemoveOnly)
|
||||
PresShell::DoUpdateApproximateFrameVisibility(bool aRemoveOnly)
|
||||
{
|
||||
MOZ_ASSERT(!mPresContext || mPresContext->IsRootContentDocument(),
|
||||
"updating image visibility on a non-root content document?");
|
||||
"Updating approximate frame visibility on a non-root content document?");
|
||||
|
||||
mUpdateImageVisibilityEvent.Revoke();
|
||||
mUpdateApproximateFrameVisibilityEvent.Revoke();
|
||||
|
||||
if (mHaveShutDown || mIsDestroying) {
|
||||
return;
|
||||
|
@ -5928,21 +5941,20 @@ PresShell::DoUpdateImageVisibility(bool aRemoveOnly)
|
|||
// call update on that frame
|
||||
nsIFrame* rootFrame = GetRootFrame();
|
||||
if (!rootFrame) {
|
||||
ClearVisibleImagesList(
|
||||
nsIImageLoadingContent::ON_NONVISIBLE_REQUEST_DISCARD);
|
||||
ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DISCARD_IMAGES));
|
||||
return;
|
||||
}
|
||||
|
||||
RebuildImageVisibility(/* aRect = */ nullptr, aRemoveOnly);
|
||||
ClearImageVisibilityVisited(rootFrame->GetView(), true);
|
||||
RebuildApproximateFrameVisibility(/* aRect = */ nullptr, aRemoveOnly);
|
||||
ClearApproximateFrameVisibilityVisited(rootFrame->GetView(), true);
|
||||
|
||||
#ifdef DEBUG_IMAGE_VISIBILITY_DISPLAY_LIST
|
||||
// This can be used to debug the frame walker by comparing beforeImageList and
|
||||
// mVisibleImages in RebuildImageVisibilityDisplayList to see if they produce
|
||||
// the same results (mVisibleImages holds the images the display list thinks
|
||||
// are visible, beforeImageList holds the images the frame walker thinks are
|
||||
// visible).
|
||||
nsDisplayListBuilder builder(rootFrame, nsDisplayListBuilder::IMAGE_VISIBILITY, false);
|
||||
#ifdef DEBUG_FRAME_VISIBILITY_DISPLAY_LIST
|
||||
// This can be used to debug the frame walker by comparing beforeFrameList
|
||||
// and mApproximatelyVisibleFrames in RebuildFrameVisibilityDisplayList to see if
|
||||
// they produce the same results (mApproximatelyVisibleFrames holds the frames the
|
||||
// display list thinks are visible, beforeFrameList holds the frames the
|
||||
// frame walker thinks are visible).
|
||||
nsDisplayListBuilder builder(rootFrame, nsDisplayListBuilder::FRAME_VISIBILITY, false);
|
||||
nsRect updateRect(nsPoint(0, 0), rootFrame->GetSize());
|
||||
nsIFrame* rootScroll = GetRootScrollFrame();
|
||||
if (rootScroll) {
|
||||
|
@ -5962,31 +5974,31 @@ PresShell::DoUpdateImageVisibility(bool aRemoveOnly)
|
|||
rootFrame->BuildDisplayListForStackingContext(&builder, updateRect, &list);
|
||||
builder.LeavePresShell(rootFrame, updateRect);
|
||||
|
||||
RebuildImageVisibilityDisplayList(list);
|
||||
RebuildApproximateFrameVisibilityDisplayList(list);
|
||||
|
||||
ClearImageVisibilityVisited(rootFrame->GetView(), true);
|
||||
ClearApproximateFrameVisibilityVisited(rootFrame->GetView(), true);
|
||||
|
||||
list.DeleteAll();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
PresShell::AssumeAllImagesVisible()
|
||||
PresShell::AssumeAllFramesVisible()
|
||||
{
|
||||
static bool sImageVisibilityEnabled = true;
|
||||
static bool sImageVisibilityPrefCached = false;
|
||||
static bool sFrameVisibilityEnabled = true;
|
||||
static bool sFrameVisibilityPrefCached = false;
|
||||
|
||||
if (!sImageVisibilityPrefCached) {
|
||||
Preferences::AddBoolVarCache(&sImageVisibilityEnabled,
|
||||
"layout.imagevisibility.enabled", true);
|
||||
sImageVisibilityPrefCached = true;
|
||||
if (!sFrameVisibilityPrefCached) {
|
||||
Preferences::AddBoolVarCache(&sFrameVisibilityEnabled,
|
||||
"layout.framevisibility.enabled", true);
|
||||
sFrameVisibilityPrefCached = true;
|
||||
}
|
||||
|
||||
if (!sImageVisibilityEnabled || !mPresContext || !mDocument) {
|
||||
if (!sFrameVisibilityEnabled || !mPresContext || !mDocument) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We assume all images are visible in print, print preview, chrome, xul, and
|
||||
// We assume all frames are visible in print, print preview, chrome, xul, and
|
||||
// resource docs and don't keep track of them.
|
||||
if (mPresContext->Type() == nsPresContext::eContext_PrintPreview ||
|
||||
mPresContext->Type() == nsPresContext::eContext_Print ||
|
||||
|
@ -6000,10 +6012,31 @@ PresShell::AssumeAllImagesVisible()
|
|||
}
|
||||
|
||||
void
|
||||
PresShell::ScheduleImageVisibilityUpdate()
|
||||
PresShell::ScheduleApproximateFrameVisibilityUpdateSoon()
|
||||
{
|
||||
if (AssumeAllImagesVisible())
|
||||
if (AssumeAllFramesVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mPresContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefreshDriver* refreshDriver = mPresContext->RefreshDriver();
|
||||
if (!refreshDriver) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ask the refresh driver to update frame visibility soon.
|
||||
refreshDriver->ScheduleFrameVisibilityUpdate();
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::ScheduleApproximateFrameVisibilityUpdateNow()
|
||||
{
|
||||
if (AssumeAllFramesVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mPresContext->IsRootContentDocument()) {
|
||||
nsPresContext* presContext = mPresContext->GetToplevelContentDocumentPresContext();
|
||||
|
@ -6011,79 +6044,89 @@ PresShell::ScheduleImageVisibilityUpdate()
|
|||
return;
|
||||
MOZ_ASSERT(presContext->IsRootContentDocument(),
|
||||
"Didn't get a root prescontext from GetToplevelContentDocumentPresContext?");
|
||||
presContext->PresShell()->ScheduleImageVisibilityUpdate();
|
||||
presContext->PresShell()->ScheduleApproximateFrameVisibilityUpdateNow();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mHaveShutDown || mIsDestroying)
|
||||
if (mHaveShutDown || mIsDestroying) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mUpdateImageVisibilityEvent.IsPending())
|
||||
if (mUpdateApproximateFrameVisibilityEvent.IsPending()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<nsRunnableMethod<PresShell> > ev =
|
||||
NS_NewRunnableMethod(this, &PresShell::UpdateImageVisibility);
|
||||
NS_NewRunnableMethod(this, &PresShell::UpdateApproximateFrameVisibility);
|
||||
if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
|
||||
mUpdateImageVisibilityEvent = ev;
|
||||
mUpdateApproximateFrameVisibilityEvent = ev;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::EnsureImageInVisibleList(nsIImageLoadingContent* aImage)
|
||||
PresShell::EnsureFrameInApproximatelyVisibleList(nsIFrame* aFrame)
|
||||
{
|
||||
if (AssumeAllImagesVisible()) {
|
||||
aImage->IncrementVisibleCount();
|
||||
if (!aFrame->TrackingVisibility()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (AssumeAllFramesVisible()) {
|
||||
aFrame->IncApproximateVisibleCount();
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// if it has a frame make sure its in this presshell
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aImage);
|
||||
// Make sure it's in this pres shell.
|
||||
nsCOMPtr<nsIContent> content = aFrame->GetContent();
|
||||
if (content) {
|
||||
PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell());
|
||||
MOZ_ASSERT(!shell || shell == this, "wrong shell");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mInImageVisibility) {
|
||||
gfxCriticalNoteOnce << "EnsureImageInVisibleList is re-entering on "
|
||||
if (mInFrameVisibilityUpdate) {
|
||||
gfxCriticalNoteOnce << "EnsureFrameInApproximatelyVisibleList is re-entering on "
|
||||
<< (NS_IsMainThread() ? "" : "non-") << "main thread";
|
||||
}
|
||||
|
||||
if (!mVisibleImages.Contains(aImage)) {
|
||||
mVisibleImages.PutEntry(aImage);
|
||||
aImage->IncrementVisibleCount();
|
||||
if (!mApproximatelyVisibleFrames.Contains(aFrame)) {
|
||||
MOZ_ASSERT(!AssumeAllFramesVisible());
|
||||
mApproximatelyVisibleFrames.PutEntry(aFrame);
|
||||
aFrame->IncApproximateVisibleCount();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::RemoveImageFromVisibleList(nsIImageLoadingContent* aImage)
|
||||
PresShell::RemoveFrameFromApproximatelyVisibleList(nsIFrame* aFrame)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// if it has a frame make sure its in this presshell
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aImage);
|
||||
// Make sure it's in this pres shell.
|
||||
nsCOMPtr<nsIContent> content = aFrame->GetContent();
|
||||
if (content) {
|
||||
PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell());
|
||||
MOZ_ASSERT(!shell || shell == this, "wrong shell");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (AssumeAllImagesVisible()) {
|
||||
MOZ_ASSERT(mVisibleImages.Count() == 0, "shouldn't have any images in the table");
|
||||
if (AssumeAllFramesVisible()) {
|
||||
MOZ_ASSERT(mApproximatelyVisibleFrames.Count() == 0,
|
||||
"Shouldn't have any frames in the table");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mInImageVisibility) {
|
||||
gfxCriticalNoteOnce << "RemoveImageFromVisibleList is re-entering on "
|
||||
if (mInFrameVisibilityUpdate) {
|
||||
gfxCriticalNoteOnce << "RemoveFrameFromApproximatelyVisibleList is re-entering on "
|
||||
<< (NS_IsMainThread() ? "" : "non-") << "main thread";
|
||||
}
|
||||
|
||||
uint32_t count = mVisibleImages.Count();
|
||||
mVisibleImages.RemoveEntry(aImage);
|
||||
if (mVisibleImages.Count() < count) {
|
||||
// aImage was in the hashtable, so we need to decrement its visible count
|
||||
aImage->DecrementVisibleCount(
|
||||
nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION);
|
||||
uint32_t count = mApproximatelyVisibleFrames.Count();
|
||||
mApproximatelyVisibleFrames.RemoveEntry(aFrame);
|
||||
|
||||
if (aFrame->TrackingVisibility() &&
|
||||
mApproximatelyVisibleFrames.Count() < count) {
|
||||
// aFrame was in the hashtable, and we're still tracking its visibility,
|
||||
// so we need to decrement its visible count.
|
||||
aFrame->DecApproximateVisibleCount();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6162,7 +6205,7 @@ PresShell::Paint(nsView* aViewToPaint,
|
|||
NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
|
||||
NS_ASSERTION(aViewToPaint, "null view");
|
||||
|
||||
MOZ_ASSERT(!mImageVisibilityVisited, "should have been cleared");
|
||||
MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "Should have been cleared");
|
||||
|
||||
if (!mIsActive || mIsZombie) {
|
||||
return;
|
||||
|
@ -8829,7 +8872,7 @@ FreezeSubDocument(nsIDocument *aDocument, void *aData)
|
|||
void
|
||||
PresShell::Freeze()
|
||||
{
|
||||
mUpdateImageVisibilityEvent.Revoke();
|
||||
mUpdateApproximateFrameVisibilityEvent.Revoke();
|
||||
|
||||
MaybeReleaseCapturingContent();
|
||||
|
||||
|
@ -9576,8 +9619,8 @@ PresShell::Observe(nsISupports* aSubject,
|
|||
}
|
||||
|
||||
if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
|
||||
if (!AssumeAllImagesVisible() && mPresContext->IsRootContentDocument()) {
|
||||
DoUpdateImageVisibility(/* aRemoveOnly = */ true);
|
||||
if (!AssumeAllFramesVisible() && mPresContext->IsRootContentDocument()) {
|
||||
DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ true);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -10852,16 +10895,15 @@ PresShell::UpdateImageLockingState()
|
|||
nsresult rv = mDocument->SetImageLockingState(locked);
|
||||
|
||||
if (locked) {
|
||||
if (mInImageVisibility) {
|
||||
if (mInFrameVisibilityUpdate) {
|
||||
gfxCriticalNoteOnce << "UpdateImageLockingState is re-entering on "
|
||||
<< (NS_IsMainThread() ? "" : "non-") << "main thread";
|
||||
}
|
||||
|
||||
// Request decodes for visible images; we want to start decoding as
|
||||
// Request decodes for visible image frames; we want to start decoding as
|
||||
// quickly as possible when we get foregrounded to minimize flashing.
|
||||
for (auto iter = mVisibleImages.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(iter.Get()->GetKey());
|
||||
nsImageFrame* imageFrame = do_QueryFrame(content->GetPrimaryFrame());
|
||||
for (auto iter = mApproximatelyVisibleFrames.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsImageFrame* imageFrame = do_QueryFrame(iter.Get()->GetKey());
|
||||
if (imageFrame) {
|
||||
imageFrame->MaybeDecodeForPredictedSize();
|
||||
}
|
||||
|
@ -10896,7 +10938,7 @@ PresShell::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
|
|||
if (mCaret) {
|
||||
*aPresShellSize += mCaret->SizeOfIncludingThis(aMallocSizeOf);
|
||||
}
|
||||
*aPresShellSize += mVisibleImages.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
*aPresShellSize += mApproximatelyVisibleFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
*aPresShellSize += mFramesToDirty.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
*aPresShellSize += aArenaObjectsSize->mOther;
|
||||
|
||||
|
|
|
@ -55,6 +55,10 @@ namespace mozilla {
|
|||
|
||||
class EventDispatchingCallback;
|
||||
|
||||
// A set type for tracking visible frames, for use by the visibility code in
|
||||
// PresShell. The set contains nsIFrame* pointers.
|
||||
typedef nsTHashtable<nsPtrHashKey<nsIFrame>> VisibleFrames;
|
||||
|
||||
// A hash table type for tracking visible regions, for use by the visibility
|
||||
// code in PresShell. The mapping is from view IDs to regions in the
|
||||
// coordinate system of that view's scrolled frame.
|
||||
|
@ -73,6 +77,9 @@ class PresShell final : public nsIPresShell,
|
|||
public nsSupportsWeakReference
|
||||
{
|
||||
template <typename T> using Maybe = mozilla::Maybe<T>;
|
||||
using Nothing = mozilla::Nothing;
|
||||
using OnNonvisible = mozilla::OnNonvisible;
|
||||
using VisibleFrames = mozilla::VisibleFrames;
|
||||
using VisibleRegions = mozilla::VisibleRegions;
|
||||
|
||||
public:
|
||||
|
@ -382,17 +389,23 @@ public:
|
|||
uint32_t mContentToScrollToFlags;
|
||||
};
|
||||
|
||||
virtual void ScheduleImageVisibilityUpdate() override;
|
||||
|
||||
virtual void RebuildImageVisibilityDisplayList(const nsDisplayList& aList) override;
|
||||
virtual void RebuildImageVisibility(nsRect* aRect = nullptr,
|
||||
bool aRemoveOnly = false) override;
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Approximate frame visibility tracking public API.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual void EnsureImageInVisibleList(nsIImageLoadingContent* aImage) override;
|
||||
void ScheduleApproximateFrameVisibilityUpdateSoon() override;
|
||||
void ScheduleApproximateFrameVisibilityUpdateNow() override;
|
||||
|
||||
virtual void RemoveImageFromVisibleList(nsIImageLoadingContent* aImage) override;
|
||||
void RebuildApproximateFrameVisibilityDisplayList(const nsDisplayList& aList) override;
|
||||
void RebuildApproximateFrameVisibility(nsRect* aRect = nullptr,
|
||||
bool aRemoveOnly = false) override;
|
||||
|
||||
void EnsureFrameInApproximatelyVisibleList(nsIFrame* aFrame) override;
|
||||
void RemoveFrameFromApproximatelyVisibleList(nsIFrame* aFrame) override;
|
||||
|
||||
bool AssumeAllFramesVisible() override;
|
||||
|
||||
virtual bool AssumeAllImagesVisible() override;
|
||||
|
||||
virtual void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot) override;
|
||||
|
||||
|
@ -402,10 +415,6 @@ public:
|
|||
|
||||
void SetNextPaintCompressed() { mNextPaintCompressed = true; }
|
||||
|
||||
void ReportAnyBadState();
|
||||
|
||||
void SetInImageVisibility(bool aState);
|
||||
|
||||
protected:
|
||||
virtual ~PresShell();
|
||||
|
||||
|
@ -742,22 +751,42 @@ protected:
|
|||
virtual void PausePainting() override;
|
||||
virtual void ResumePainting() override;
|
||||
|
||||
void UpdateImageVisibility();
|
||||
void DoUpdateImageVisibility(bool aRemoveOnly);
|
||||
void UpdateActivePointerState(mozilla::WidgetGUIEvent* aEvent);
|
||||
|
||||
nsRevocableEventPtr<nsRunnableMethod<PresShell> > mUpdateImageVisibilityEvent;
|
||||
|
||||
void ClearVisibleImagesList(uint32_t aNonvisibleAction);
|
||||
static void ClearImageVisibilityVisited(nsView* aView, bool aClear);
|
||||
static void MarkImagesInListVisible(const nsDisplayList& aList,
|
||||
Maybe<VisibleRegions>& aVisibleRegions);
|
||||
void MarkImagesInSubtreeVisible(nsIFrame* aFrame,
|
||||
const nsRect& aRect,
|
||||
Maybe<VisibleRegions>& aVisibleRegions,
|
||||
bool aRemoveOnly = false);
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Approximate frame visibility tracking implementation.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void UpdateApproximateFrameVisibility();
|
||||
void DoUpdateApproximateFrameVisibility(bool aRemoveOnly);
|
||||
|
||||
void ClearApproximatelyVisibleFramesList(Maybe<mozilla::OnNonvisible> aNonvisibleAction
|
||||
= Nothing());
|
||||
static void ClearApproximateFrameVisibilityVisited(nsView* aView, bool aClear);
|
||||
static void MarkFramesInListApproximatelyVisible(const nsDisplayList& aList,
|
||||
Maybe<VisibleRegions>& aVisibleRegions);
|
||||
void MarkFramesInSubtreeApproximatelyVisible(nsIFrame* aFrame,
|
||||
const nsRect& aRect,
|
||||
Maybe<VisibleRegions>& aVisibleRegions,
|
||||
bool aRemoveOnly = false);
|
||||
|
||||
void DecApproximateVisibleCount(VisibleFrames& aFrames,
|
||||
Maybe<OnNonvisible> aNonvisibleAction = Nothing());
|
||||
void ReportBadStateDuringVisibilityUpdate();
|
||||
void SetInFrameVisibilityUpdate(bool aState);
|
||||
|
||||
nsRevocableEventPtr<nsRunnableMethod<PresShell>> mUpdateApproximateFrameVisibilityEvent;
|
||||
|
||||
// A set of frames that were visible or could be visible soon at the time
|
||||
// that we last did an approximate frame visibility update.
|
||||
VisibleFrames mApproximatelyVisibleFrames;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Methods for dispatching KeyboardEvent and BeforeAfterKeyboardEvent.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void HandleKeyboardEvent(nsINode* aTarget,
|
||||
mozilla::WidgetKeyboardEvent& aEvent,
|
||||
bool aEmbeddedCancelled,
|
||||
|
@ -775,9 +804,6 @@ protected:
|
|||
size_t aChainIndex = 0);
|
||||
bool CanDispatchEvent(const mozilla::WidgetGUIEvent* aEvent = nullptr) const;
|
||||
|
||||
// A list of images that are visible or almost visible.
|
||||
nsTHashtable< nsRefPtrHashKey<nsIImageLoadingContent> > mVisibleImages;
|
||||
|
||||
nsresult SetResolutionImpl(float aResolution, bool aScaleToResolution);
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -887,7 +913,7 @@ protected:
|
|||
bool mAsyncResizeTimerIsActive : 1;
|
||||
bool mInResize : 1;
|
||||
|
||||
bool mImageVisibilityVisited : 1;
|
||||
bool mApproximateFrameVisibilityVisited : 1;
|
||||
|
||||
bool mNextPaintCompressed : 1;
|
||||
|
||||
|
@ -904,7 +930,7 @@ protected:
|
|||
// Whether the widget has received a paint message yet.
|
||||
bool mHasReceivedPaintMessage : 1;
|
||||
|
||||
bool mInImageVisibility : 1;
|
||||
bool mInFrameVisibilityUpdate : 1;
|
||||
|
||||
static bool sDisableNonTestMouseEvents;
|
||||
};
|
||||
|
|
|
@ -1804,15 +1804,15 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
|||
}
|
||||
}
|
||||
|
||||
// Recompute image visibility if it's necessary and enough time has passed
|
||||
// since the last time we did it.
|
||||
// Recompute approximate frame visibility if it's necessary and enough time
|
||||
// has passed since the last time we did it.
|
||||
if (mNeedToRecomputeVisibility && !mThrottled &&
|
||||
aNowTime >= mNextRecomputeVisibilityTick &&
|
||||
!presShell->IsPaintingSuppressed()) {
|
||||
mNextRecomputeVisibilityTick = aNowTime + mMinRecomputeVisibilityInterval;
|
||||
mNeedToRecomputeVisibility = false;
|
||||
|
||||
presShell->ScheduleImageVisibilityUpdate();
|
||||
presShell->ScheduleApproximateFrameVisibilityUpdateNow();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -237,6 +237,12 @@ public:
|
|||
*/
|
||||
void CancelPendingEvents(nsIDocument* aDocument);
|
||||
|
||||
/**
|
||||
* Schedule a frame visibility update "soon", subject to the heuristics and
|
||||
* throttling we apply to visibility updates.
|
||||
*/
|
||||
void ScheduleFrameVisibilityUpdate() { mNeedToRecomputeVisibility = true; }
|
||||
|
||||
/**
|
||||
* Tell the refresh driver that it is done driving refreshes and
|
||||
* should stop its timer and forget about its pres context. This may
|
||||
|
@ -392,10 +398,10 @@ private:
|
|||
// non-visible) documents registered with a non-throttled refresh driver.
|
||||
const mozilla::TimeDuration mThrottledFrameRequestInterval;
|
||||
|
||||
// How long we wait, at a minimum, before recomputing image visibility
|
||||
// information. This is a minimum because, regardless of this interval, we
|
||||
// only recompute visibility when we've seen a layout or style flush since the
|
||||
// last time we did it.
|
||||
// How long we wait, at a minimum, before recomputing approximate frame
|
||||
// visibility information. This is a minimum because, regardless of this
|
||||
// interval, we only recompute visibility when we've seen a layout or style
|
||||
// flush since the last time we did it.
|
||||
const mozilla::TimeDuration mMinRecomputeVisibilityInterval;
|
||||
|
||||
bool mThrottled;
|
||||
|
|
|
@ -721,10 +721,11 @@ TextOverflow::CanHaveTextOverflow(nsDisplayListBuilder* aBuilder,
|
|||
nsIFrame* aBlockFrame)
|
||||
{
|
||||
// Nothing to do for text-overflow:clip or if 'overflow-x/y:visible' or if
|
||||
// we're just building items for event processing or image visibility.
|
||||
// we're just building items for event processing or frame visibility.
|
||||
if (HasClippedOverflow(aBlockFrame) ||
|
||||
IsInlineAxisOverflowVisible(aBlockFrame) ||
|
||||
aBuilder->IsForEventDelivery() || aBuilder->IsForImageVisibility()) {
|
||||
aBuilder->IsForEventDelivery() ||
|
||||
aBuilder->IsForFrameVisibility()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/**
|
||||
* Declares visibility-related types. @Visibility is an enumeration of the
|
||||
* possible visibility states of a frame. @OnNonvisible is an enumeration that
|
||||
* allows callers to request a specific action when a frame transitions from
|
||||
* visible to nonvisible.
|
||||
*/
|
||||
|
||||
#ifndef mozilla_layout_generic_Visibility_h
|
||||
#define mozilla_layout_generic_Visibility_h
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Visibility states for frames.
|
||||
enum class Visibility : uint8_t
|
||||
{
|
||||
// Indicates that we're not tracking visibility for this frame.
|
||||
UNTRACKED,
|
||||
|
||||
// Indicates that the frame is probably nonvisible. Visible frames *may* be
|
||||
// APPROXIMATELY_NONVISIBLE because approximate visibility is not updated
|
||||
// synchronously. Some truly nonvisible frames may be marked
|
||||
// APPROXIMATELY_VISIBLE instead if our heuristics lead us to think they may
|
||||
// be visible soon.
|
||||
APPROXIMATELY_NONVISIBLE,
|
||||
|
||||
// Indicates that the frame is either visible now or is likely to be visible
|
||||
// soon according to our heuristics. As with APPROXIMATELY_NONVISIBLE, it's
|
||||
// important to note that approximately visibility is not updated
|
||||
// synchronously, so this information may be out of date.
|
||||
APPROXIMATELY_VISIBLE
|
||||
};
|
||||
|
||||
// Requested actions when frames transition to the nonvisible state.
|
||||
enum class OnNonvisible : uint8_t
|
||||
{
|
||||
DISCARD_IMAGES // Discard images associated with the frame.
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_layout_generic_Visibility_h
|
|
@ -96,6 +96,7 @@ EXPORTS += [
|
|||
'nsTextRunTransformations.h',
|
||||
'RubyUtils.h',
|
||||
'ScrollbarActivity.h',
|
||||
'Visibility.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
|
|
|
@ -411,6 +411,9 @@ nsFrame::~nsFrame()
|
|||
{
|
||||
MOZ_COUNT_DTOR(nsFrame);
|
||||
|
||||
MOZ_ASSERT(GetVisibility() != Visibility::APPROXIMATELY_VISIBLE,
|
||||
"Visible nsFrame is being destroyed");
|
||||
|
||||
NS_IF_RELEASE(mContent);
|
||||
#ifdef DEBUG
|
||||
mStyleContext->FrameRelease();
|
||||
|
@ -544,6 +547,11 @@ nsFrame::Init(nsIContent* aContent,
|
|||
NS_FRAME_IS_SVG_TEXT |
|
||||
NS_FRAME_IN_POPUP |
|
||||
NS_FRAME_IS_NONDISPLAY);
|
||||
|
||||
if (HasAnyStateBits(NS_FRAME_IN_POPUP) && TrackingVisibility()) {
|
||||
// Assume all frames in popups are visible.
|
||||
IncApproximateVisibleCount();
|
||||
}
|
||||
}
|
||||
const nsStyleDisplay *disp = StyleDisplay();
|
||||
if (disp->HasTransform(this)) {
|
||||
|
@ -593,6 +601,11 @@ nsFrame::Init(nsIContent* aContent,
|
|||
AddStateBits(NS_FRAME_HAS_VR_CONTENT);
|
||||
}
|
||||
|
||||
if (PresContext()->PresShell()->AssumeAllFramesVisible() &&
|
||||
TrackingVisibility()) {
|
||||
IncApproximateVisibleCount();
|
||||
}
|
||||
|
||||
DidSetStyleContext(nullptr);
|
||||
|
||||
if (::IsBoxWrapped(this))
|
||||
|
@ -702,6 +715,17 @@ nsFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
|||
}
|
||||
}
|
||||
|
||||
// Disable visibility tracking. Note that we have to do this before calling
|
||||
// NotifyDestroyingFrame(), which will clear frame properties and make us lose
|
||||
// track of whether we were previously visible or not.
|
||||
// XXX(seth): It'd be ideal to assert that we're already marked nonvisible
|
||||
// here, but it's unfortunately tricky to guarantee in the face of things like
|
||||
// frame reconstruction induced by style changes.
|
||||
DisableVisibilityTracking();
|
||||
|
||||
// Ensure that we're not in the approximately visible list anymore.
|
||||
PresContext()->GetPresShell()->RemoveFrameFromApproximatelyVisibleList(this);
|
||||
|
||||
shell->NotifyDestroyingFrame(this);
|
||||
|
||||
if (mState & NS_FRAME_EXTERNAL_REFERENCE) {
|
||||
|
@ -1442,6 +1466,190 @@ nsIFrame::GetCrossDocChildLists(nsTArray<ChildList>* aLists)
|
|||
GetChildLists(aLists);
|
||||
}
|
||||
|
||||
Visibility
|
||||
nsIFrame::GetVisibility() const
|
||||
{
|
||||
if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
|
||||
return Visibility::UNTRACKED;
|
||||
}
|
||||
|
||||
bool isSet = false;
|
||||
FrameProperties props = Properties();
|
||||
uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
|
||||
|
||||
MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
|
||||
"if NS_FRAME_VISIBILITY_IS_TRACKED is set");
|
||||
|
||||
return visibleCount > 0
|
||||
? Visibility::APPROXIMATELY_VISIBLE
|
||||
: Visibility::APPROXIMATELY_NONVISIBLE;
|
||||
}
|
||||
|
||||
void
|
||||
nsIFrame::UpdateVisibilitySynchronously()
|
||||
{
|
||||
nsIPresShell* presShell = PresContext()->PresShell();
|
||||
if (!presShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (presShell->AssumeAllFramesVisible()) {
|
||||
presShell->EnsureFrameInApproximatelyVisibleList(this);
|
||||
return;
|
||||
}
|
||||
|
||||
bool visible = true;
|
||||
nsIFrame* f = GetParent();
|
||||
nsRect rect = GetRectRelativeToSelf();
|
||||
nsIFrame* rectFrame = this;
|
||||
while (f) {
|
||||
nsIScrollableFrame* sf = do_QueryFrame(f);
|
||||
if (sf) {
|
||||
nsRect transformedRect =
|
||||
nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f);
|
||||
if (!sf->IsRectNearlyVisible(transformedRect)) {
|
||||
visible = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// In this code we're trying to synchronously update *approximate*
|
||||
// visibility. (In the future we may update precise visibility here as
|
||||
// well, which is why the method name does not contain 'approximate'.) The
|
||||
// IsRectNearlyVisible() check above tells us that the rect we're checking
|
||||
// is approximately visible within the scrollframe, but we still need to
|
||||
// ensure that, even if it was scrolled into view, it'd be visible when we
|
||||
// consider the rest of the document. To do that, we move transformedRect
|
||||
// to be contained in the scrollport as best we can (it might not fit) to
|
||||
// pretend that it was scrolled into view.
|
||||
rect = transformedRect.MoveInsideAndClamp(sf->GetScrollPortRect());
|
||||
rectFrame = f;
|
||||
}
|
||||
nsIFrame* parent = f->GetParent();
|
||||
if (!parent) {
|
||||
parent = nsLayoutUtils::GetCrossDocParentFrame(f);
|
||||
if (parent && parent->PresContext()->IsChrome()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
f = parent;
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
presShell->EnsureFrameInApproximatelyVisibleList(this);
|
||||
} else {
|
||||
presShell->RemoveFrameFromApproximatelyVisibleList(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsIFrame::EnableVisibilityTracking()
|
||||
{
|
||||
if (GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED) {
|
||||
return; // Nothing to do.
|
||||
}
|
||||
|
||||
FrameProperties props = Properties();
|
||||
MOZ_ASSERT(!props.Has(VisibilityStateProperty()),
|
||||
"Shouldn't have a VisibilityStateProperty value "
|
||||
"if NS_FRAME_VISIBILITY_IS_TRACKED is not set");
|
||||
|
||||
// Add the state bit so we know to track visibility for this frame, and
|
||||
// initialize the frame property.
|
||||
AddStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
|
||||
props.Set(VisibilityStateProperty(), 0);
|
||||
|
||||
nsIPresShell* presShell = PresContext()->PresShell();
|
||||
if (!presShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Schedule a visibility update. This method will virtually always be called
|
||||
// when layout has changed anyway, so it's very unlikely that any additional
|
||||
// visibility updates will be triggered by this, but this way we guarantee
|
||||
// that if this frame is currently visible we'll eventually find out.
|
||||
presShell->ScheduleApproximateFrameVisibilityUpdateSoon();
|
||||
}
|
||||
|
||||
void
|
||||
nsIFrame::DisableVisibilityTracking()
|
||||
{
|
||||
if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
|
||||
return; // Nothing to do.
|
||||
}
|
||||
|
||||
bool isSet = false;
|
||||
FrameProperties props = Properties();
|
||||
uint32_t visibleCount = props.Remove(VisibilityStateProperty(), &isSet);
|
||||
|
||||
MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
|
||||
"if NS_FRAME_VISIBILITY_IS_TRACKED is set");
|
||||
|
||||
RemoveStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
|
||||
|
||||
if (visibleCount == 0) {
|
||||
return; // We were nonvisible.
|
||||
}
|
||||
|
||||
// We were visible, so send an OnVisibilityChange() notification.
|
||||
OnVisibilityChange(Visibility::APPROXIMATELY_NONVISIBLE);
|
||||
}
|
||||
|
||||
void
|
||||
nsIFrame::DecApproximateVisibleCount(Maybe<OnNonvisible> aNonvisibleAction
|
||||
/* = Nothing() */)
|
||||
{
|
||||
MOZ_ASSERT(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
|
||||
|
||||
bool isSet = false;
|
||||
FrameProperties props = Properties();
|
||||
uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
|
||||
|
||||
MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
|
||||
"if NS_FRAME_VISIBILITY_IS_TRACKED is set");
|
||||
MOZ_ASSERT(visibleCount > 0, "Frame is already nonvisible and we're "
|
||||
"decrementing its visible count?");
|
||||
|
||||
visibleCount--;
|
||||
props.Set(VisibilityStateProperty(), visibleCount);
|
||||
if (visibleCount > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We just became nonvisible, so send an OnVisibilityChange() notification.
|
||||
OnVisibilityChange(Visibility::APPROXIMATELY_NONVISIBLE, aNonvisibleAction);
|
||||
}
|
||||
|
||||
void
|
||||
nsIFrame::IncApproximateVisibleCount()
|
||||
{
|
||||
MOZ_ASSERT(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
|
||||
|
||||
bool isSet = false;
|
||||
FrameProperties props = Properties();
|
||||
uint32_t visibleCount = props.Get(VisibilityStateProperty(), &isSet);
|
||||
|
||||
MOZ_ASSERT(isSet, "Should have a VisibilityStateProperty value "
|
||||
"if NS_FRAME_VISIBILITY_IS_TRACKED is set");
|
||||
|
||||
visibleCount++;
|
||||
props.Set(VisibilityStateProperty(), visibleCount);
|
||||
if (visibleCount > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We just became visible, so send an OnVisibilityChange() notification.
|
||||
OnVisibilityChange(Visibility::APPROXIMATELY_VISIBLE);
|
||||
}
|
||||
|
||||
void
|
||||
nsIFrame::OnVisibilityChange(Visibility aNewVisibility,
|
||||
Maybe<OnNonvisible> aNonvisibleAction
|
||||
/* = Nothing() */)
|
||||
{
|
||||
// XXX(seth): In bug 1218990 we'll implement visibility tracking for CSS
|
||||
// images here.
|
||||
}
|
||||
|
||||
static nsIFrame*
|
||||
GetActiveSelectionFrame(nsPresContext* aPresContext, nsIFrame* aFrame)
|
||||
{
|
||||
|
@ -8939,6 +9147,12 @@ nsFrame::BoxMetrics() const
|
|||
/* static */ void
|
||||
nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame)
|
||||
{
|
||||
if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) &&
|
||||
aFrame->TrackingVisibility()) {
|
||||
// Assume all frames in popups are visible.
|
||||
aFrame->IncApproximateVisibleCount();
|
||||
}
|
||||
|
||||
aFrame->AddStateBits(NS_FRAME_IN_POPUP);
|
||||
|
||||
AutoTArray<nsIFrame::ChildList,4> childListArray;
|
||||
|
@ -8963,6 +9177,12 @@ nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame)
|
|||
|
||||
aFrame->RemoveStateBits(NS_FRAME_IN_POPUP);
|
||||
|
||||
if (aFrame->TrackingVisibility()) {
|
||||
// We assume all frames in popups are visible, so this decrement balances
|
||||
// out the increment in AddInPopupStateBitToDescendants above.
|
||||
aFrame->DecApproximateVisibleCount();
|
||||
}
|
||||
|
||||
AutoTArray<nsIFrame::ChildList,4> childListArray;
|
||||
aFrame->GetCrossDocChildLists(&childListArray);
|
||||
|
||||
|
|
|
@ -229,6 +229,10 @@ FRAME_STATE_BIT(Generic, 44, NS_FRAME_MAY_HAVE_GENERATED_CONTENT)
|
|||
// the frames for future reference.
|
||||
FRAME_STATE_BIT(Generic, 45, NS_FRAME_NO_COMPONENT_ALPHA)
|
||||
|
||||
// This bit indicates that we're tracking visibility for this frame, and that
|
||||
// the frame has a VisibilityStateProperty property.
|
||||
FRAME_STATE_BIT(Generic, 46, NS_FRAME_VISIBILITY_IS_TRACKED)
|
||||
|
||||
// The frame is a descendant of SVGTextFrame and is thus used for SVG
|
||||
// text layout.
|
||||
FRAME_STATE_BIT(Generic, 47, NS_FRAME_IS_SVG_TEXT)
|
||||
|
|
|
@ -1862,9 +1862,9 @@ ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter,
|
|||
, mRestorePos(-1, -1)
|
||||
, mLastPos(-1, -1)
|
||||
, mScrollPosForLayerPixelAlignment(-1, -1)
|
||||
, mLastUpdateImagesPos(-1, -1)
|
||||
, mHadDisplayPortAtLastImageUpdate(false)
|
||||
, mDisplayPortAtLastImageUpdate()
|
||||
, mLastUpdateFramesPos(-1, -1)
|
||||
, mHadDisplayPortAtLastFrameUpdate(false)
|
||||
, mDisplayPortAtLastFrameUpdate()
|
||||
, mNeverHasVerticalScrollbar(false)
|
||||
, mNeverHasHorizontalScrollbar(false)
|
||||
, mHasVerticalScrollbar(false)
|
||||
|
@ -1898,7 +1898,7 @@ ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter,
|
|||
mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(aOuter));
|
||||
}
|
||||
|
||||
EnsureImageVisPrefsCached();
|
||||
EnsureFrameVisPrefsCached();
|
||||
|
||||
if (IsAlwaysActive() &&
|
||||
gfxPrefs::LayersTilesEnabled() &&
|
||||
|
@ -2633,20 +2633,21 @@ ScrollFrameHelper::ScheduleSyntheticMouseMove()
|
|||
}
|
||||
|
||||
void
|
||||
ScrollFrameHelper::NotifyImageVisibilityUpdate()
|
||||
ScrollFrameHelper::NotifyApproximateFrameVisibilityUpdate()
|
||||
{
|
||||
mLastUpdateImagesPos = GetScrollPosition();
|
||||
mHadDisplayPortAtLastImageUpdate =
|
||||
nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &mDisplayPortAtLastImageUpdate);
|
||||
mLastUpdateFramesPos = GetScrollPosition();
|
||||
mHadDisplayPortAtLastFrameUpdate =
|
||||
nsLayoutUtils::GetDisplayPort(mOuter->GetContent(),
|
||||
&mDisplayPortAtLastFrameUpdate);
|
||||
}
|
||||
|
||||
bool
|
||||
ScrollFrameHelper::GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort)
|
||||
ScrollFrameHelper::GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort)
|
||||
{
|
||||
if (mHadDisplayPortAtLastImageUpdate) {
|
||||
*aDisplayPort = mDisplayPortAtLastImageUpdate;
|
||||
if (mHadDisplayPortAtLastFrameUpdate) {
|
||||
*aDisplayPort = mDisplayPortAtLastFrameUpdate;
|
||||
}
|
||||
return mHadDisplayPortAtLastImageUpdate;
|
||||
return mHadDisplayPortAtLastFrameUpdate;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2688,17 +2689,17 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOri
|
|||
return;
|
||||
}
|
||||
|
||||
bool needImageVisibilityUpdate = (mLastUpdateImagesPos == nsPoint(-1,-1));
|
||||
bool needFrameVisibilityUpdate = mLastUpdateFramesPos == nsPoint(-1,-1);
|
||||
|
||||
nsPoint dist(std::abs(pt.x - mLastUpdateImagesPos.x),
|
||||
std::abs(pt.y - mLastUpdateImagesPos.y));
|
||||
nsPoint dist(std::abs(pt.x - mLastUpdateFramesPos.x),
|
||||
std::abs(pt.y - mLastUpdateFramesPos.y));
|
||||
nsSize scrollPortSize = GetScrollPositionClampingScrollPortSize();
|
||||
nscoord horzAllowance = std::max(scrollPortSize.width / std::max(sHorzScrollFraction, 1),
|
||||
nsPresContext::AppUnitsPerCSSPixel());
|
||||
nscoord vertAllowance = std::max(scrollPortSize.height / std::max(sVertScrollFraction, 1),
|
||||
nsPresContext::AppUnitsPerCSSPixel());
|
||||
if (dist.x >= horzAllowance || dist.y >= vertAllowance) {
|
||||
needImageVisibilityUpdate = true;
|
||||
needFrameVisibilityUpdate = true;
|
||||
}
|
||||
|
||||
// notify the listeners.
|
||||
|
@ -2767,8 +2768,8 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOri
|
|||
if (schedulePaint) {
|
||||
mOuter->SchedulePaint();
|
||||
|
||||
if (needImageVisibilityUpdate) {
|
||||
presContext->PresShell()->ScheduleImageVisibilityUpdate();
|
||||
if (needFrameVisibilityUpdate) {
|
||||
presContext->PresShell()->ScheduleApproximateFrameVisibilityUpdateNow();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2979,27 +2980,27 @@ ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder,
|
|||
}
|
||||
}
|
||||
|
||||
/* static */ bool ScrollFrameHelper::sImageVisPrefsCached = false;
|
||||
/* static */ bool ScrollFrameHelper::sFrameVisPrefsCached = false;
|
||||
/* static */ uint32_t ScrollFrameHelper::sHorzExpandScrollPort = 0;
|
||||
/* static */ uint32_t ScrollFrameHelper::sVertExpandScrollPort = 1;
|
||||
/* static */ int32_t ScrollFrameHelper::sHorzScrollFraction = 2;
|
||||
/* static */ int32_t ScrollFrameHelper::sVertScrollFraction = 2;
|
||||
|
||||
/* static */ void
|
||||
ScrollFrameHelper::EnsureImageVisPrefsCached()
|
||||
ScrollFrameHelper::EnsureFrameVisPrefsCached()
|
||||
{
|
||||
if (!sImageVisPrefsCached) {
|
||||
if (!sFrameVisPrefsCached) {
|
||||
Preferences::AddUintVarCache(&sHorzExpandScrollPort,
|
||||
"layout.imagevisibility.numscrollportwidths", (uint32_t)0);
|
||||
"layout.framevisibility.numscrollportwidths", (uint32_t)0);
|
||||
Preferences::AddUintVarCache(&sVertExpandScrollPort,
|
||||
"layout.imagevisibility.numscrollportheights", 1);
|
||||
"layout.framevisibility.numscrollportheights", 1);
|
||||
|
||||
Preferences::AddIntVarCache(&sHorzScrollFraction,
|
||||
"layout.imagevisibility.amountscrollbeforeupdatehorizontal", 2);
|
||||
"layout.framevisibility.amountscrollbeforeupdatehorizontal", 2);
|
||||
Preferences::AddIntVarCache(&sVertScrollFraction,
|
||||
"layout.imagevisibility.amountscrollbeforeupdatevertical", 2);
|
||||
"layout.framevisibility.amountscrollbeforeupdatevertical", 2);
|
||||
|
||||
sImageVisPrefsCached = true;
|
||||
sFrameVisPrefsCached = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3103,8 +3104,8 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
const nsRect& aDirtyRect,
|
||||
const nsDisplayListSet& aLists)
|
||||
{
|
||||
if (aBuilder->IsForImageVisibility()) {
|
||||
NotifyImageVisibilityUpdate();
|
||||
if (aBuilder->IsForFrameVisibility()) {
|
||||
NotifyApproximateFrameVisibilityUpdate();
|
||||
}
|
||||
|
||||
mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists);
|
||||
|
@ -3148,8 +3149,8 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
bool usingDisplayPort = aBuilder->IsPaintingToWindow() &&
|
||||
nsLayoutUtils::HasDisplayPort(mOuter->GetContent());
|
||||
|
||||
if (aBuilder->IsForImageVisibility()) {
|
||||
// We expand the dirty rect to catch images just outside of the scroll port.
|
||||
if (aBuilder->IsForFrameVisibility()) {
|
||||
// We expand the dirty rect to catch frames just outside of the scroll port.
|
||||
// We use the dirty rect instead of the whole scroll port to prevent
|
||||
// too much expansion in the presence of very large (bigger than the
|
||||
// viewport) scroll ports.
|
||||
|
|
|
@ -393,8 +393,8 @@ public:
|
|||
bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
|
||||
nsRect* aDirtyRect,
|
||||
bool aAllowCreateDisplayPort);
|
||||
void NotifyImageVisibilityUpdate();
|
||||
bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort);
|
||||
void NotifyApproximateFrameVisibilityUpdate();
|
||||
bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort);
|
||||
|
||||
bool AllowDisplayPortExpiration();
|
||||
void TriggerDisplayPortExpiration();
|
||||
|
@ -492,10 +492,10 @@ public:
|
|||
nsCOMPtr<nsITimer> mScrollActivityTimer;
|
||||
nsPoint mScrollPosForLayerPixelAlignment;
|
||||
|
||||
// The scroll position where we last updated image visibility.
|
||||
nsPoint mLastUpdateImagesPos;
|
||||
bool mHadDisplayPortAtLastImageUpdate;
|
||||
nsRect mDisplayPortAtLastImageUpdate;
|
||||
// The scroll position where we last updated frame visibility.
|
||||
nsPoint mLastUpdateFramesPos;
|
||||
bool mHadDisplayPortAtLastFrameUpdate;
|
||||
nsRect mDisplayPortAtLastFrameUpdate;
|
||||
|
||||
nsRect mPrevScrolledRect;
|
||||
|
||||
|
@ -625,13 +625,13 @@ protected:
|
|||
AsyncScrollEventType mAsyncScrollEvent;
|
||||
bool HasPluginFrames();
|
||||
|
||||
static void EnsureImageVisPrefsCached();
|
||||
static bool sImageVisPrefsCached;
|
||||
// The number of scrollports wide/high to expand when looking for images.
|
||||
static void EnsureFrameVisPrefsCached();
|
||||
static bool sFrameVisPrefsCached;
|
||||
// The number of scrollports wide/high to expand when tracking frame visibility.
|
||||
static uint32_t sHorzExpandScrollPort;
|
||||
static uint32_t sVertExpandScrollPort;
|
||||
// The fraction of the scrollport we allow to scroll by before we schedule
|
||||
// an update of image visibility.
|
||||
// an update of frame visibility.
|
||||
static int32_t sHorzScrollFraction;
|
||||
static int32_t sVertScrollFraction;
|
||||
};
|
||||
|
@ -926,11 +926,11 @@ public:
|
|||
bool aAllowCreateDisplayPort) override {
|
||||
return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
|
||||
}
|
||||
virtual void NotifyImageVisibilityUpdate() override {
|
||||
mHelper.NotifyImageVisibilityUpdate();
|
||||
virtual void NotifyApproximateFrameVisibilityUpdate() override {
|
||||
mHelper.NotifyApproximateFrameVisibilityUpdate();
|
||||
}
|
||||
virtual bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort) override {
|
||||
return mHelper.GetDisplayPortAtLastImageVisibilityUpdate(aDisplayPort);
|
||||
virtual bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort) override {
|
||||
return mHelper.GetDisplayPortAtLastApproximateFrameVisibilityUpdate(aDisplayPort);
|
||||
}
|
||||
void TriggerDisplayPortExpiration() override {
|
||||
mHelper.TriggerDisplayPortExpiration();
|
||||
|
@ -1419,11 +1419,11 @@ public:
|
|||
bool aAllowCreateDisplayPort) override {
|
||||
return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
|
||||
}
|
||||
virtual void NotifyImageVisibilityUpdate() override {
|
||||
mHelper.NotifyImageVisibilityUpdate();
|
||||
virtual void NotifyApproximateFrameVisibilityUpdate() override {
|
||||
mHelper.NotifyApproximateFrameVisibilityUpdate();
|
||||
}
|
||||
virtual bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort) override {
|
||||
return mHelper.GetDisplayPortAtLastImageVisibilityUpdate(aDisplayPort);
|
||||
virtual bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort) override {
|
||||
return mHelper.GetDisplayPortAtLastApproximateFrameVisibilityUpdate(aDisplayPort);
|
||||
}
|
||||
void TriggerDisplayPortExpiration() override {
|
||||
mHelper.TriggerDisplayPortExpiration();
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "CaretAssociationHint.h"
|
||||
#include "FramePropertyTable.h"
|
||||
#include "mozilla/layout/FrameChildList.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/WritingModes.h"
|
||||
#include "nsDirection.h"
|
||||
#include "nsFrameList.h"
|
||||
|
@ -37,6 +38,7 @@
|
|||
#include "nsStringGlue.h"
|
||||
#include "nsStyleContext.h"
|
||||
#include "nsStyleStruct.h"
|
||||
#include "Visibility.h"
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
#include "mozilla/a11y/AccTypes.h"
|
||||
|
@ -416,8 +418,12 @@ static void ReleaseValue(T* aPropertyValue)
|
|||
class nsIFrame : public nsQueryFrame
|
||||
{
|
||||
public:
|
||||
template <typename T> using Maybe = mozilla::Maybe<T>;
|
||||
using Nothing = mozilla::Nothing;
|
||||
using OnNonvisible = mozilla::OnNonvisible;
|
||||
template<typename T=void>
|
||||
using PropertyDescriptor = const mozilla::FramePropertyDescriptor<T>*;
|
||||
using Visibility = mozilla::Visibility;
|
||||
|
||||
typedef mozilla::FrameProperties FrameProperties;
|
||||
typedef mozilla::layers::Layer Layer;
|
||||
|
@ -1081,6 +1087,94 @@ public:
|
|||
return GetLogicalBaseline(GetWritingMode());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// The public visibility API.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// @return true if we're tracking visibility for this frame.
|
||||
bool TrackingVisibility() const
|
||||
{
|
||||
return bool(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
|
||||
}
|
||||
|
||||
/// @return the visibility state of this frame. See the Visibility enum
|
||||
/// for the possible return values and their meanings.
|
||||
Visibility GetVisibility() const;
|
||||
|
||||
/// Update the visibility state of this frame synchronously.
|
||||
/// XXX(seth): Avoid using this method; we should be relying on the refresh
|
||||
/// driver for visibility updates. This method, which replaces
|
||||
/// nsLayoutUtils::UpdateApproximateFrameVisibility(), exists purely as a
|
||||
/// temporary measure to avoid changing behavior during the transition from
|
||||
/// the old image visibility code.
|
||||
void UpdateVisibilitySynchronously();
|
||||
|
||||
// A frame property which stores the visibility state of this frame. Right
|
||||
// now that consists of an approximate visibility counter represented as a
|
||||
// uint32_t. When the visibility of this frame is not being tracked, this
|
||||
// property is absent.
|
||||
NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(VisibilityStateProperty, uint32_t);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Subclasses can call this method to enable visibility tracking for this frame.
|
||||
*
|
||||
* If visibility tracking was previously disabled, this will schedule an
|
||||
* update an asynchronous update of visibility.
|
||||
*/
|
||||
void EnableVisibilityTracking();
|
||||
|
||||
/**
|
||||
* Subclasses can call this method to disable visibility tracking for this frame.
|
||||
*
|
||||
* Note that if visibility tracking was previously enabled, disabling visibility
|
||||
* tracking will cause a synchronous call to OnVisibilityChange().
|
||||
*/
|
||||
void DisableVisibilityTracking();
|
||||
|
||||
/**
|
||||
* Called when a frame transitions between visibility states (for example,
|
||||
* from nonvisible to visible, or from visible to nonvisible).
|
||||
*
|
||||
* @param aNewVisibility The new visibility state.
|
||||
* @param aNonvisibleAction A requested action if the frame has become
|
||||
* nonvisible. If Nothing(), no action is
|
||||
* requested. If DISCARD_IMAGES is specified, the
|
||||
* frame is requested to ask any images it's
|
||||
* associated with to discard their surfaces if
|
||||
* possible.
|
||||
*
|
||||
* Subclasses which override this method should call their parent class's
|
||||
* implementation.
|
||||
*/
|
||||
virtual void OnVisibilityChange(Visibility aNewVisibility,
|
||||
Maybe<OnNonvisible> aNonvisibleAction = Nothing());
|
||||
|
||||
public:
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Internal implementation for the approximate frame visibility API.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* We track the approximate visibility of frames using a counter; if it's
|
||||
* non-zero, then the frame is considered visible. Using a counter allows us
|
||||
* to account for situations where the frame may be visible in more than one
|
||||
* place (for example, via -moz-element), and it simplifies the
|
||||
* implementation of our approximate visibility tracking algorithms.
|
||||
*
|
||||
* @param aNonvisibleAction A requested action if the frame has become
|
||||
* nonvisible. If Nothing(), no action is
|
||||
* requested. If DISCARD_IMAGES is specified, the
|
||||
* frame is requested to ask any images it's
|
||||
* associated with to discard their surfaces if
|
||||
* possible.
|
||||
*/
|
||||
void DecApproximateVisibleCount(Maybe<OnNonvisible> aNonvisibleAction = Nothing());
|
||||
void IncApproximateVisibleCount();
|
||||
|
||||
|
||||
/**
|
||||
* Get the specified child list.
|
||||
*
|
||||
|
|
|
@ -347,13 +347,13 @@ public:
|
|||
*/
|
||||
virtual void ClearDidHistoryRestore() = 0;
|
||||
/**
|
||||
* Determine if the passed in rect is nearly visible according to the image
|
||||
* Determine if the passed in rect is nearly visible according to the frame
|
||||
* visibility heuristics for how close it is to the visible scrollport.
|
||||
*/
|
||||
virtual bool IsRectNearlyVisible(const nsRect& aRect) = 0;
|
||||
/**
|
||||
* Expand the given rect taking into account which directions we can scroll
|
||||
* and how far we want to expand for image visibility purposes.
|
||||
* and how far we want to expand for frame visibility purposes.
|
||||
*/
|
||||
virtual nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const = 0;
|
||||
/**
|
||||
|
@ -453,16 +453,16 @@ public:
|
|||
bool aAllowCreateDisplayPort) = 0;
|
||||
|
||||
/**
|
||||
* Notification that this scroll frame is getting its image visibility updated.
|
||||
* Notification that this scroll frame is getting its frame visibility updated.
|
||||
*/
|
||||
virtual void NotifyImageVisibilityUpdate() = 0;
|
||||
virtual void NotifyApproximateFrameVisibilityUpdate() = 0;
|
||||
|
||||
/**
|
||||
* Returns true if this scroll frame had a display port at the last image
|
||||
* Returns true if this scroll frame had a display port at the last frame
|
||||
* visibility update and fills in aDisplayPort with that displayport. Returns
|
||||
* false otherwise, and doesn't touch aDisplayPort.
|
||||
*/
|
||||
virtual bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort) = 0;
|
||||
virtual bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort) = 0;
|
||||
|
||||
/**
|
||||
* This is called when a descendant scrollframe's has its displayport expired.
|
||||
|
|
|
@ -144,6 +144,8 @@ nsImageFrame::nsImageFrame(nsStyleContext* aContext) :
|
|||
mReflowCallbackPosted(false),
|
||||
mForceSyncDecoding(false)
|
||||
{
|
||||
EnableVisibilityTracking();
|
||||
|
||||
// We assume our size is not constrained and we haven't gotten an
|
||||
// initial reflow yet, so don't touch those flags.
|
||||
mIntrinsicSize.width.SetCoordValue(0);
|
||||
|
@ -712,9 +714,7 @@ nsImageFrame::MaybeDecodeForPredictedSize()
|
|||
return; // We won't draw anything, so no point in decoding.
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
|
||||
MOZ_ASSERT(imageLoader);
|
||||
if (imageLoader->GetVisibleCount() == 0) {
|
||||
if (GetVisibility() != Visibility::APPROXIMATELY_VISIBLE) {
|
||||
return; // We're not visible, so don't decode.
|
||||
}
|
||||
|
||||
|
@ -1041,7 +1041,16 @@ nsImageFrame::ReflowFinished()
|
|||
{
|
||||
mReflowCallbackPosted = false;
|
||||
|
||||
nsLayoutUtils::UpdateImageVisibilityForFrame(this);
|
||||
// XXX(seth): We don't need this. The purpose of updating visibility
|
||||
// synchronously is to ensure that animated images start animating
|
||||
// immediately. In the short term, however,
|
||||
// nsImageLoadingContent::OnUnlockedDraw() is enough to ensure that
|
||||
// animations start as soon as the image is painted for the first time, and in
|
||||
// the long term we want to update visibility information from the display
|
||||
// list whenever we paint, so we don't actually need to do this. However, to
|
||||
// avoid behavior changes during the transition from the old image visibility
|
||||
// code, we'll leave it in for now.
|
||||
UpdateVisibilitySynchronously();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -2135,6 +2144,25 @@ nsImageFrame::AttributeChanged(int32_t aNameSpaceID,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsImageFrame::OnVisibilityChange(Visibility aNewVisibility,
|
||||
Maybe<OnNonvisible> aNonvisibleAction)
|
||||
{
|
||||
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
|
||||
if (!imageLoader) {
|
||||
MOZ_ASSERT_UNREACHABLE("Should have an nsIImageLoadingContent");
|
||||
return;
|
||||
}
|
||||
|
||||
imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
|
||||
|
||||
if (aNewVisibility == Visibility::APPROXIMATELY_VISIBLE) {
|
||||
MaybeDecodeForPredictedSize();
|
||||
}
|
||||
|
||||
ImageFrameSuper::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
|
||||
}
|
||||
|
||||
nsIAtom*
|
||||
nsImageFrame::GetType() const
|
||||
{
|
||||
|
|
|
@ -63,6 +63,10 @@ typedef nsAtomicContainerFrame ImageFrameSuper;
|
|||
class nsImageFrame : public ImageFrameSuper,
|
||||
public nsIReflowCallback {
|
||||
public:
|
||||
template <typename T> using Maybe = mozilla::Maybe<T>;
|
||||
using Nothing = mozilla::Nothing;
|
||||
using Visibility = mozilla::Visibility;
|
||||
|
||||
typedef mozilla::image::DrawResult DrawResult;
|
||||
typedef mozilla::layers::ImageContainer ImageContainer;
|
||||
typedef mozilla::layers::ImageLayer ImageLayer;
|
||||
|
@ -104,6 +108,9 @@ public:
|
|||
nsIAtom* aAttribute,
|
||||
int32_t aModType) override;
|
||||
|
||||
void OnVisibilityChange(Visibility aNewVisibility,
|
||||
Maybe<OnNonvisible> aNonvisibleAction = Nothing()) override;
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
virtual mozilla::a11y::AccType AccessibleType() override;
|
||||
#endif
|
||||
|
|
|
@ -571,9 +571,9 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
childItems.AppendToTop(layerItem);
|
||||
}
|
||||
|
||||
if (aBuilder->IsForImageVisibility()) {
|
||||
if (aBuilder->IsForFrameVisibility()) {
|
||||
// We don't add the childItems to the return list as we're dealing with them here.
|
||||
presShell->RebuildImageVisibilityDisplayList(childItems);
|
||||
presShell->RebuildApproximateFrameVisibilityDisplayList(childItems);
|
||||
childItems.DeleteAll();
|
||||
} else {
|
||||
aLists.Content()->AppendToTop(&childItems);
|
||||
|
|
|
@ -41,9 +41,10 @@ NS_NewHTMLVideoFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
|||
|
||||
NS_IMPL_FRAMEARENA_HELPERS(nsVideoFrame)
|
||||
|
||||
nsVideoFrame::nsVideoFrame(nsStyleContext* aContext) :
|
||||
nsContainerFrame(aContext)
|
||||
nsVideoFrame::nsVideoFrame(nsStyleContext* aContext)
|
||||
: nsVideoFrameBase(aContext)
|
||||
{
|
||||
EnableVisibilityTracking();
|
||||
}
|
||||
|
||||
nsVideoFrame::~nsVideoFrame()
|
||||
|
@ -53,7 +54,7 @@ nsVideoFrame::~nsVideoFrame()
|
|||
NS_QUERYFRAME_HEAD(nsVideoFrame)
|
||||
NS_QUERYFRAME_ENTRY(nsVideoFrame)
|
||||
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
|
||||
NS_QUERYFRAME_TAIL_INHERITING(nsVideoFrameBase)
|
||||
|
||||
nsresult
|
||||
nsVideoFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
|
||||
|
@ -144,7 +145,7 @@ nsVideoFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
|||
nsContentUtils::DestroyAnonymousContent(&mCaptionDiv);
|
||||
nsContentUtils::DestroyAnonymousContent(&mVideoControls);
|
||||
nsContentUtils::DestroyAnonymousContent(&mPosterImage);
|
||||
nsContainerFrame::DestroyFrom(aDestructRoot);
|
||||
nsVideoFrameBase::DestroyFrom(aDestructRoot);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -611,11 +612,25 @@ nsVideoFrame::AttributeChanged(int32_t aNameSpaceID,
|
|||
if (aAttribute == nsGkAtoms::poster && HasVideoElement()) {
|
||||
UpdatePosterSource(true);
|
||||
}
|
||||
return nsContainerFrame::AttributeChanged(aNameSpaceID,
|
||||
return nsVideoFrameBase::AttributeChanged(aNameSpaceID,
|
||||
aAttribute,
|
||||
aModType);
|
||||
}
|
||||
|
||||
void
|
||||
nsVideoFrame::OnVisibilityChange(Visibility aNewVisibility,
|
||||
Maybe<OnNonvisible> aNonvisibleAction)
|
||||
{
|
||||
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mPosterImage);
|
||||
if (!imageLoader) {
|
||||
return;
|
||||
}
|
||||
|
||||
imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
|
||||
|
||||
nsVideoFrameBase::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
|
||||
}
|
||||
|
||||
bool nsVideoFrame::HasVideoElement() {
|
||||
nsCOMPtr<nsIDOMHTMLMediaElement> mediaDomElement = do_QueryInterface(mContent);
|
||||
return mediaDomElement->IsVideo();
|
||||
|
|
|
@ -26,9 +26,15 @@ class nsAString;
|
|||
class nsPresContext;
|
||||
class nsDisplayItem;
|
||||
|
||||
class nsVideoFrame : public nsContainerFrame, public nsIAnonymousContentCreator
|
||||
typedef nsContainerFrame nsVideoFrameBase;
|
||||
|
||||
class nsVideoFrame : public nsVideoFrameBase, public nsIAnonymousContentCreator
|
||||
{
|
||||
public:
|
||||
template <typename T> using Maybe = mozilla::Maybe<T>;
|
||||
using Nothing = mozilla::Nothing;
|
||||
using Visibility = mozilla::Visibility;
|
||||
|
||||
typedef mozilla::layers::Layer Layer;
|
||||
typedef mozilla::layers::LayerManager LayerManager;
|
||||
typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
|
||||
|
@ -47,6 +53,9 @@ public:
|
|||
nsIAtom* aAttribute,
|
||||
int32_t aModType) override;
|
||||
|
||||
void OnVisibilityChange(Visibility aNewVisibility,
|
||||
Maybe<OnNonvisible> aNonvisibleAction = Nothing()) override;
|
||||
|
||||
/* get the size of the video's display */
|
||||
nsSize GetVideoIntrinsicSize(nsRenderingContext *aRenderingContext);
|
||||
virtual nsSize GetIntrinsicRatio() override;
|
||||
|
|
|
@ -27,6 +27,12 @@ protected:
|
|||
: SVGFEImageFrameBase(aContext)
|
||||
{
|
||||
AddStateBits(NS_FRAME_SVG_LAYOUT | NS_FRAME_IS_NONDISPLAY);
|
||||
|
||||
// This frame isn't actually displayed, but it contains an image and we want
|
||||
// to use the nsImageLoadingContent machinery for managing images, which
|
||||
// requires visibility tracking, so we enable visibility tracking and
|
||||
// forcibly mark it visible below.
|
||||
EnableVisibilityTracking();
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -60,6 +66,9 @@ public:
|
|||
nsIAtom* aAttribute,
|
||||
int32_t aModType) override;
|
||||
|
||||
void OnVisibilityChange(Visibility aNewVisibility,
|
||||
Maybe<OnNonvisible> aNonvisibleAction = Nothing()) override;
|
||||
|
||||
virtual bool UpdateOverflow() override {
|
||||
// We don't maintain a visual overflow rect
|
||||
return false;
|
||||
|
@ -77,13 +86,12 @@ NS_IMPL_FRAMEARENA_HELPERS(SVGFEImageFrame)
|
|||
/* virtual */ void
|
||||
SVGFEImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
||||
{
|
||||
DecApproximateVisibleCount();
|
||||
|
||||
nsCOMPtr<nsIImageLoadingContent> imageLoader =
|
||||
do_QueryInterface(SVGFEImageFrameBase::mContent);
|
||||
|
||||
if (imageLoader) {
|
||||
imageLoader->FrameDestroyed(this);
|
||||
imageLoader
|
||||
->DecrementVisibleCount(nsIImageLoadingContent::ON_NONVISIBLE_NO_ACTION);
|
||||
}
|
||||
|
||||
SVGFEImageFrameBase::DestroyFrom(aDestructRoot);
|
||||
|
@ -99,14 +107,13 @@ SVGFEImageFrame::Init(nsIContent* aContent,
|
|||
"content element that doesn't support the right interfaces");
|
||||
|
||||
SVGFEImageFrameBase::Init(aContent, aParent, aPrevInFlow);
|
||||
|
||||
// We assume that feImage's are always visible.
|
||||
IncApproximateVisibleCount();
|
||||
|
||||
nsCOMPtr<nsIImageLoadingContent> imageLoader =
|
||||
do_QueryInterface(SVGFEImageFrameBase::mContent);
|
||||
|
||||
if (imageLoader) {
|
||||
// We assume that feImage's are always visible.
|
||||
// Increment the visible count before calling FrameCreated so that
|
||||
// FrameCreated will actually track the image correctly.
|
||||
imageLoader->IncrementVisibleCount();
|
||||
imageLoader->FrameCreated(this);
|
||||
}
|
||||
}
|
||||
|
@ -140,3 +147,19 @@ SVGFEImageFrame::AttributeChanged(int32_t aNameSpaceID,
|
|||
return SVGFEImageFrameBase::AttributeChanged(aNameSpaceID,
|
||||
aAttribute, aModType);
|
||||
}
|
||||
|
||||
void
|
||||
SVGFEImageFrame::OnVisibilityChange(Visibility aNewVisibility,
|
||||
Maybe<OnNonvisible> aNonvisibleAction)
|
||||
{
|
||||
nsCOMPtr<nsIImageLoadingContent> imageLoader =
|
||||
do_QueryInterface(SVGFEImageFrameBase::mContent);
|
||||
if (!imageLoader) {
|
||||
MOZ_ASSERT_UNREACHABLE("Should have an nsIImageLoadingContent");
|
||||
return;
|
||||
}
|
||||
|
||||
imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
|
||||
|
||||
SVGFEImageFrameBase::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
|
||||
}
|
||||
|
|
|
@ -54,8 +54,13 @@ class nsSVGImageFrame : public nsSVGImageFrameBase,
|
|||
NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
|
||||
|
||||
protected:
|
||||
explicit nsSVGImageFrame(nsStyleContext* aContext) : nsSVGImageFrameBase(aContext),
|
||||
mReflowCallbackPosted(false) {}
|
||||
explicit nsSVGImageFrame(nsStyleContext* aContext)
|
||||
: nsSVGImageFrameBase(aContext)
|
||||
, mReflowCallbackPosted(false)
|
||||
{
|
||||
EnableVisibilityTracking();
|
||||
}
|
||||
|
||||
virtual ~nsSVGImageFrame();
|
||||
|
||||
public:
|
||||
|
@ -75,6 +80,10 @@ public:
|
|||
virtual nsresult AttributeChanged(int32_t aNameSpaceID,
|
||||
nsIAtom* aAttribute,
|
||||
int32_t aModType) override;
|
||||
|
||||
void OnVisibilityChange(Visibility aNewVisibility,
|
||||
Maybe<OnNonvisible> aNonvisibleAction = Nothing()) override;
|
||||
|
||||
virtual void Init(nsIContent* aContent,
|
||||
nsContainerFrame* aParent,
|
||||
nsIFrame* aPrevInFlow) override;
|
||||
|
@ -222,6 +231,20 @@ nsSVGImageFrame::AttributeChanged(int32_t aNameSpaceID,
|
|||
aAttribute, aModType);
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGImageFrame::OnVisibilityChange(Visibility aNewVisibility,
|
||||
Maybe<OnNonvisible> aNonvisibleAction)
|
||||
{
|
||||
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
|
||||
if (!imageLoader) {
|
||||
return;
|
||||
}
|
||||
|
||||
imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
|
||||
|
||||
nsSVGImageFrameBase::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
|
||||
}
|
||||
|
||||
gfx::Matrix
|
||||
nsSVGImageFrame::GetRasterImageTransform(int32_t aNativeWidth,
|
||||
int32_t aNativeHeight)
|
||||
|
@ -526,7 +549,16 @@ nsSVGImageFrame::ReflowFinished()
|
|||
{
|
||||
mReflowCallbackPosted = false;
|
||||
|
||||
nsLayoutUtils::UpdateImageVisibilityForFrame(this);
|
||||
// XXX(seth): We don't need this. The purpose of updating visibility
|
||||
// synchronously is to ensure that animated images start animating
|
||||
// immediately. In the short term, however,
|
||||
// nsImageLoadingContent::OnUnlockedDraw() is enough to ensure that
|
||||
// animations start as soon as the image is painted for the first time, and in
|
||||
// the long term we want to update visibility information from the display
|
||||
// list whenever we paint, so we don't actually need to do this. However, to
|
||||
// avoid behavior changes during the transition from the old image visibility
|
||||
// code, we'll leave it in for now.
|
||||
UpdateVisibilitySynchronously();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -803,8 +803,8 @@ pref("app.orientation.default", "");
|
|||
// back to the system.
|
||||
pref("memory.free_dirty_pages", true);
|
||||
|
||||
pref("layout.imagevisibility.numscrollportwidths", 1);
|
||||
pref("layout.imagevisibility.numscrollportheights", 1);
|
||||
pref("layout.framevisibility.numscrollportwidths", 1);
|
||||
pref("layout.framevisibility.numscrollportheights", 1);
|
||||
|
||||
pref("layers.enable-tiles", true);
|
||||
|
||||
|
|
|
@ -952,11 +952,11 @@ pref("nglayout.debug.widget_update_flashing", false);
|
|||
// Enable/disable display list invalidation logging --- useful for debugging.
|
||||
pref("nglayout.debug.invalidation", false);
|
||||
|
||||
// Whether image visibility is enabled globally (ie we will try to unlock images
|
||||
// that are not visible).
|
||||
pref("layout.imagevisibility.enabled", true);
|
||||
pref("layout.imagevisibility.numscrollportwidths", 0);
|
||||
pref("layout.imagevisibility.numscrollportheights", 1);
|
||||
// Whether frame visibility tracking is enabled globally.
|
||||
pref("layout.framevisibility.enabled", true);
|
||||
|
||||
pref("layout.framevisibility.numscrollportwidths", 0);
|
||||
pref("layout.framevisibility.numscrollportheights", 1);
|
||||
|
||||
// scrollbar snapping region
|
||||
// 0 - off
|
||||
|
|
Загрузка…
Ссылка в новой задаче