Bug 539356 - Part 9b - Add new frame invalidation API. r=roc

This commit is contained in:
Matt Woodrow 2012-08-29 17:39:01 +12:00
parent c900597192
commit cc33f42c8a
8 changed files with 289 additions and 12 deletions

View File

@ -1017,6 +1017,8 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
nsRefPtr<ContainerLayer> root = layerBuilder->
BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nullptr, *this,
containerParameters, nullptr);
aForFrame->ClearInvalidationStateBits();
if (!root) {
layerManager->RemoveUserData(&gLayerManagerLayerBuilder);

View File

@ -4001,6 +4001,8 @@ nsLayoutUtils::IsPopup(nsIFrame* aFrame)
/* static */ nsIFrame*
nsLayoutUtils::GetDisplayRootFrame(nsIFrame* aFrame)
{
// We could use GetRootPresContext() here if the
// NS_FRAME_IN_POPUP frame bit is set.
nsIFrame* f = aFrame;
for (;;) {
if (IsPopup(f))

View File

@ -1000,6 +1000,8 @@ nsListControlFrame::Init(nsIContent* aContent,
mLastDropdownBackstopColor = PresContext()->DefaultBackgroundColor();
AddStateBits(NS_FRAME_IN_POPUP);
return result;
}

View File

@ -85,6 +85,7 @@
#include "nsChangeHint.h"
#include "nsDeckFrame.h"
#include "nsTableFrame.h"
#include "nsSubDocumentFrame.h"
#include "gfxContext.h"
#include "nsRenderingContext.h"
@ -504,7 +505,8 @@ nsFrame::Init(nsIContent* aContent,
// Make bits that are currently off (see constructor) the same:
mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
NS_FRAME_GENERATED_CONTENT |
NS_FRAME_IS_SVG_TEXT);
NS_FRAME_IS_SVG_TEXT |
NS_FRAME_IN_POPUP);
}
const nsStyleDisplay *disp = GetStyleDisplay();
if (disp->HasTransform()) {
@ -1268,6 +1270,23 @@ nsFrame::GetChildLists(nsTArray<ChildList>* aLists) const
}
}
void
nsIFrame::GetCrossDocChildLists(nsTArray<ChildList>* aLists)
{
nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this);
if (subdocumentFrame) {
// Descend into the subdocument
nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
if (root) {
aLists->AppendElement(nsIFrame::ChildList(
nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
nsIFrame::kPrincipalList));
}
}
GetChildLists(aLists);
}
static nsIFrame*
GetActiveSelectionFrame(nsPresContext* aPresContext, nsIFrame* aFrame)
{
@ -4906,10 +4925,60 @@ nsIFrame::InvalidateRectDifference(const nsRect& aR1, const nsRect& aR2)
}
void
nsIFrame::InvalidateFrameSubtree()
nsIFrame::InvalidateFrameSubtree(uint32_t aFlags)
{
Invalidate(GetVisualOverflowRectRelativeToSelf());
FrameLayerBuilder::InvalidateThebesLayersInSubtree(this);
InvalidateFrame(aFlags);
nsAutoTArray<nsIFrame::ChildList,4> childListArray;
GetCrossDocChildLists(&childListArray);
nsIFrame::ChildListArrayIterator lists(childListArray);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
childFrames.get()->
InvalidateFrameSubtree(aFlags | INVALIDATE_DONT_SCHEDULE_PAINT);
}
}
}
void
nsIFrame::ClearInvalidationStateBits()
{
if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
nsAutoTArray<nsIFrame::ChildList,4> childListArray;
GetCrossDocChildLists(&childListArray);
nsIFrame::ChildListArrayIterator lists(childListArray);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
childFrames.get()->ClearInvalidationStateBits();
}
}
}
RemoveStateBits(NS_FRAME_NEEDS_PAINT | NS_FRAME_DESCENDANT_NEEDS_PAINT);
}
void
nsIFrame::InvalidateFrame(uint32_t aFlags)
{
AddStateBits(NS_FRAME_NEEDS_PAINT);
nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(this);
while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
}
if (!(aFlags & INVALIDATE_DONT_SCHEDULE_PAINT)) {
SchedulePaint();
}
}
bool
nsIFrame::IsInvalid()
{
return HasAnyStateBits(NS_FRAME_NEEDS_PAINT);
}
void
@ -4918,6 +4987,36 @@ nsIFrame::InvalidateOverflowRect()
Invalidate(GetVisualOverflowRectRelativeToSelf());
}
void
nsIFrame::SchedulePaint()
{
nsPresContext *pres = PresContext()->GetRootPresContext();
if (HasAnyStateBits(NS_FRAME_IN_POPUP) || !pres) {
nsIFrame *displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
NS_ASSERTION(displayRoot, "Need a display root to schedule a paint!");
if (!displayRoot) {
return;
}
pres = displayRoot->PresContext();
}
pres->PresShell()->ScheduleViewManagerFlush();
}
Layer*
nsIFrame::InvalidateLayer(const nsRect& aDamageRect, uint32_t aDisplayItemKey)
{
NS_ASSERTION(aDisplayItemKey > 0, "Need a key");
Layer* layer = FrameLayerBuilder::GetDedicatedLayer(this, aDisplayItemKey);
if (!layer) {
InvalidateFrame();
return nullptr;
}
SchedulePaint();
return layer;
}
NS_DECLARE_FRAME_PROPERTY(DeferInvalidatesProperty, nsIFrame::DestroyRegion)
void
@ -8147,6 +8246,55 @@ nsFrame::BoxMetrics() const
return metrics;
}
/**
* Adds the NS_FRAME_IN_POPUP state bit to the current frame,
* and all descendant frames (including cross-doc ones).
*/
static void
AddInPopupStateBitToDescendants(nsIFrame* aFrame)
{
aFrame->AddStateBits(NS_FRAME_IN_POPUP);
nsAutoTArray<nsIFrame::ChildList,4> childListArray;
aFrame->GetCrossDocChildLists(&childListArray);
nsIFrame::ChildListArrayIterator lists(childListArray);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
AddInPopupStateBitToDescendants(childFrames.get());
}
}
}
/**
* Removes the NS_FRAME_IN_POPUP state bit from the current
* frames and all descendant frames (including cross-doc ones),
* unless the frame is a popup itself.
*/
static void
RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame)
{
if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) ||
aFrame->GetType() == nsGkAtoms::listControlFrame ||
aFrame->GetType() == nsGkAtoms::menuPopupFrame) {
return;
}
aFrame->RemoveStateBits(NS_FRAME_IN_POPUP);
nsAutoTArray<nsIFrame::ChildList,4> childListArray;
aFrame->GetCrossDocChildLists(&childListArray);
nsIFrame::ChildListArrayIterator lists(childListArray);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
RemoveInPopupStateBitFromDescendants(childFrames.get());
}
}
}
void
nsFrame::SetParent(nsIFrame* aParent)
{
@ -8165,6 +8313,20 @@ nsFrame::SetParent(nsIFrame* aParent)
f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
}
}
if (HasInvalidFrameInSubtree()) {
for (nsIFrame* f = aParent;
f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
}
}
if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
AddInPopupStateBitToDescendants(this);
} else {
RemoveInPopupStateBitFromDescendants(this);
}
if (GetStateBits() & NS_FRAME_HAS_CONTAINER_LAYER_DESCENDANT) {
for (nsIFrame* f = aParent;

View File

@ -311,6 +311,16 @@ typedef uint64_t nsFrameState;
// text layout.
#define NS_FRAME_IS_SVG_TEXT NS_FRAME_STATE_BIT(47)
// Frame is marked as needing painting
#define NS_FRAME_NEEDS_PAINT NS_FRAME_STATE_BIT(48)
// Frame has a descendant frame that needs painting - This includes
// cross-doc children.
#define NS_FRAME_DESCENDANT_NEEDS_PAINT NS_FRAME_STATE_BIT(49)
// Frame is a descendant of a popup
#define NS_FRAME_IN_POPUP NS_FRAME_STATE_BIT(50)
// Box layout bits
#define NS_STATE_IS_HORIZONTAL NS_FRAME_STATE_BIT(22)
#define NS_STATE_IS_DIRECTION_NORMAL NS_FRAME_STATE_BIT(31)
@ -1076,6 +1086,13 @@ public:
virtual const nsFrameList& GetChildList(ChildListID aListID) const = 0;
const nsFrameList& PrincipalChildList() { return GetChildList(kPrincipalList); }
virtual void GetChildLists(nsTArray<ChildList>* aLists) const = 0;
/**
* Gets the child lists for this frame, including
* ones belong to a child document.
*/
void GetCrossDocChildLists(nsTArray<ChildList>* aLists);
// XXXbz this method should go away
nsIFrame* GetFirstChild(ChildListID aListID) const {
return GetChildList(aListID).FirstChild();
@ -1399,6 +1416,16 @@ public:
void AddStateBits(nsFrameState aBits) { mState |= aBits; }
void RemoveStateBits(nsFrameState aBits) { mState &= ~aBits; }
/**
* Checks if the current frame-state includes all of the listed bits
*/
bool HasAllStateBits(nsFrameState aBits) { return (mState & aBits) == aBits; }
/**
* Checks if the current frame-state includes any of the listed bits
*/
bool HasAnyStateBits(nsFrameState aBits) { return mState & aBits; }
/**
* This call is invoked on the primary frame for a character data content
* node, when it is changed in the content tree.
@ -2268,13 +2295,6 @@ public:
*/
void InvalidateRectDifference(const nsRect& aR1, const nsRect& aR2);
/**
* Invalidate the entire frame subtree for this frame. Invalidates this
* frame's overflow rect, and also ensures that all ThebesLayer children
* of ContainerLayers associated with frames in this subtree are
* completely invalidated.
*/
void InvalidateFrameSubtree();
/**
* Invalidates this frame's visual overflow rect. Does not necessarily
@ -2283,6 +2303,73 @@ public:
*/
void InvalidateOverflowRect();
/**
* Marks all display items created by this frame as needing a repaint,
* and calls SchedulePaint() if requested.
*
* This includes all display items created by this frame, including
* container types.
* @param aFlags INVALIDATE_DONT_SCHEDULE_PAINT: Don't call SchedulePaint()
* when invalidating.
*/
enum {
INVALIDATE_DONT_SCHEDULE_PAINT
};
virtual void InvalidateFrame(uint32_t aFlags = 0);
/**
* Calls InvalidateFrame() on all frames descendant frames (including
* this one).
*
* This function doesn't walk through placeholder frames to invalidate
* the out-of-flow frames.
*/
void InvalidateFrameSubtree(uint32_t aFlags = 0);
/**
* Checks if a frame has had InvalidateFrame() called on it since the
* last paint.
*/
bool IsInvalid();
/**
* Check if any frame within the frame subtree (including this frame)
* returns true for IsInvalid().
*/
bool HasInvalidFrameInSubtree()
{
return HasAnyStateBits(NS_FRAME_NEEDS_PAINT | NS_FRAME_DESCENDANT_NEEDS_PAINT);
}
/**
* Removes the invalid state from the current frame and all
* descendant frames.
*/
void ClearInvalidationStateBits();
/**
* Ensures that the refresh driver is running, and schedules a view
* manager flush on the next tick.
*
* The view manager flush will update the layer tree, repaint any
* invalid areas in the layer tree and schedule a layer tree
* composite operation to display the layer tree.
*/
void SchedulePaint();
/**
* Checks if the layer tree includes a dedicated layer for this
* frame/display item key pair, and invalidates at least aDamageRect
* area within that layer.
*
* If no layer is found, calls InvalidateFrame() instead.
*
* @param aDamageRect Area of the layer to invalidate.
* @param aDisplayItemKey Display item type.
* @return Layer, if found, nullptr otherwise.
*/
Layer* InvalidateLayer(const nsRect& aDamageRect, uint32_t aDisplayItemKey);
/**
* Returns a rect that encompasses everything that might be painted by
* this frame. This includes this frame, all its descendent frames, this

View File

@ -954,6 +954,9 @@ nsSubDocumentFrame::BeginSwapDocShells(nsIFrame* aOther)
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_ASSERTION(HasAnyStateBits(NS_FRAME_IN_POPUP) == other->HasAnyStateBits(NS_FRAME_IN_POPUP),
"Can't swap doc shells when only one is within a popup!");
if (mInnerView && other->mInnerView) {
nsIView* ourSubdocViews = mInnerView->GetFirstChild();
nsIView* ourRemovedViews = ::BeginSwapDocShellsForViews(ourSubdocViews);
@ -1008,6 +1011,14 @@ EndSwapDocShellsForViews(nsIView* aSibling)
if (doc) {
::EndSwapDocShellsForDocument(doc, nullptr);
}
nsIFrame *frame = aSibling->GetFrame();
if (frame && frame->HasInvalidFrameInSubtree()) {
nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(frame);
while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
}
}
}
}

View File

@ -34,7 +34,16 @@ ViewportFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow)
{
return Super::Init(aContent, aParent, aPrevInFlow);
nsresult rv = Super::Init(aContent, aParent, aPrevInFlow);
nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(this);
if (parent) {
nsFrameState state = parent->GetStateBits();
mState |= state & (NS_FRAME_IN_POPUP);
}
return rv;
}
void

View File

@ -170,6 +170,8 @@ nsMenuPopupFrame::Init(nsIContent* aContent,
}
}
AddStateBits(NS_FRAME_IN_POPUP);
return rv;
}