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:
Seth Fowler 2016-03-25 14:49:43 -07:00
Родитель 020510839b
Коммит 5973113f1b
30 изменённых файлов: 1006 добавлений и 506 удалений

Просмотреть файл

@ -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