Bug 945203. Part 4: Add nsDisplayEventRegions and build it when painting. r=mats

--HG--
extra : rebase_source : 26b71b6a18681e8fc48c6082c8df6263c1bd3739
This commit is contained in:
Robert O'Callahan 2013-12-17 00:22:11 +13:00
parent b842ce7261
commit 67dd0d7a70
5 changed files with 167 additions and 3 deletions

View File

@ -22,6 +22,7 @@ DECLARE_DISPLAY_ITEM_TYPE(CHECKED_RADIOBUTTON)
DECLARE_DISPLAY_ITEM_TYPE(COLUMN_RULE)
DECLARE_DISPLAY_ITEM_TYPE(COMBOBOX_FOCUS)
DECLARE_DISPLAY_ITEM_TYPE(EVENT_RECEIVER)
DECLARE_DISPLAY_ITEM_TYPE(LAYER_EVENT_REGIONS)
DECLARE_DISPLAY_ITEM_TYPE(FIELDSET_BORDER_BACKGROUND)
DECLARE_DISPLAY_ITEM_TYPE(STICKY_POSITION)
DECLARE_DISPLAY_ITEM_TYPE(FORCEPAINTONSCROLL)

View File

@ -474,6 +474,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
Mode aMode, bool aBuildCaret)
: mReferenceFrame(aReferenceFrame),
mIgnoreScrollFrame(nullptr),
mLayerEventRegions(nullptr),
mCurrentTableItem(nullptr),
mFinalTransparentRegion(nullptr),
mCachedOffsetFrame(aReferenceFrame),
@ -496,7 +497,8 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mIsPaintingToWindow(false),
mIsCompositingCheap(false),
mContainsPluginItem(false),
mContainsBlendMode(false)
mContainsBlendMode(false),
mAncestorHasTouchEventHandler(false)
{
MOZ_COUNT_CTOR(nsDisplayListBuilder);
PL_InitArenaPool(&mPool, "displayListArena", 1024,
@ -2498,6 +2500,40 @@ nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
aOutFrames->AppendElement(mFrame);
}
void
nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame)
{
NS_ASSERTION(aBuilder->FindReferenceFrameFor(aFrame) == aBuilder->FindReferenceFrameFor(mFrame),
"Reference frame mismatch");
uint8_t pointerEvents = aFrame->StyleVisibility()->mPointerEvents;
if (pointerEvents == NS_STYLE_POINTER_EVENTS_NONE) {
return;
}
// XXX handle other pointerEvents values for SVG
// XXX Do something clever here for the common case where the border box
// is obviously entirely inside mHitRegion.
nsRect borderBox(aBuilder->ToReferenceFrame(aFrame), aFrame->GetSize());
const DisplayItemClip* clip = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder);
bool borderBoxHasRoundedCorners =
nsLayoutUtils::HasNonZeroCorner(aFrame->StyleBorder()->mBorderRadius);
if (clip) {
borderBox = clip->ApplyNonRoundedIntersection(borderBox);
if (clip->GetRoundedRectCount() > 0) {
borderBoxHasRoundedCorners = true;
}
}
if (borderBoxHasRoundedCorners ||
(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
mMaybeHitRegion.Or(mMaybeHitRegion, borderBox);
} else {
mHitRegion.Or(mHitRegion, borderBox);
}
if (aBuilder->GetAncestorHasTouchEventHandler()) {
mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, borderBox);
}
}
void
nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) {

View File

@ -35,6 +35,7 @@ class nsIContent;
class nsRenderingContext;
class nsDisplayTableItem;
class nsISelection;
class nsDisplayLayerEventRegions;
namespace mozilla {
namespace layers {
@ -296,6 +297,24 @@ public:
bool AllowMergingAndFlattening() { return mAllowMergingAndFlattening; }
void SetAllowMergingAndFlattening(bool aAllow) { mAllowMergingAndFlattening = aAllow; }
nsDisplayLayerEventRegions* GetLayerEventRegions() { return mLayerEventRegions; }
void SetLayerEventRegions(nsDisplayLayerEventRegions* aItem)
{
mLayerEventRegions = aItem;
}
bool IsBuildingLayerEventRegions()
{
// Disable for now.
return false;
// return mMode == PAINTING;
}
bool GetAncestorHasTouchEventHandler() { return mAncestorHasTouchEventHandler; }
void SetAncestorHasTouchEventHandler(bool aValue)
{
mAncestorHasTouchEventHandler = aValue;
}
bool SetIsCompositingCheap(bool aCompositingCheap) {
bool temp = mIsCompositingCheap;
mIsCompositingCheap = aCompositingCheap;
@ -488,8 +507,11 @@ public:
: mBuilder(aBuilder),
mPrevCachedOffsetFrame(aBuilder->mCachedOffsetFrame),
mPrevCachedReferenceFrame(aBuilder->mCachedReferenceFrame),
mPrevLayerEventRegions(aBuilder->mLayerEventRegions),
mPrevCachedOffset(aBuilder->mCachedOffset),
mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext) {
mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext),
mPrevAncestorHasTouchEventHandler(aBuilder->mAncestorHasTouchEventHandler)
{
aBuilder->mIsAtRootOfPseudoStackingContext = aIsRoot;
}
AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder,
@ -497,8 +519,10 @@ public:
: mBuilder(aBuilder),
mPrevCachedOffsetFrame(aBuilder->mCachedOffsetFrame),
mPrevCachedReferenceFrame(aBuilder->mCachedReferenceFrame),
mPrevLayerEventRegions(aBuilder->mLayerEventRegions),
mPrevCachedOffset(aBuilder->mCachedOffset),
mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext)
mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext),
mPrevAncestorHasTouchEventHandler(aBuilder->mAncestorHasTouchEventHandler)
{
if (aForChild->IsTransformed()) {
aBuilder->mCachedOffset = nsPoint();
@ -514,15 +538,19 @@ public:
~AutoBuildingDisplayList() {
mBuilder->mCachedOffsetFrame = mPrevCachedOffsetFrame;
mBuilder->mCachedReferenceFrame = mPrevCachedReferenceFrame;
mBuilder->mLayerEventRegions = mPrevLayerEventRegions;
mBuilder->mCachedOffset = mPrevCachedOffset;
mBuilder->mIsAtRootOfPseudoStackingContext = mPrevIsAtRootOfPseudoStackingContext;
mBuilder->mAncestorHasTouchEventHandler = mPrevAncestorHasTouchEventHandler;
}
private:
nsDisplayListBuilder* mBuilder;
const nsIFrame* mPrevCachedOffsetFrame;
const nsIFrame* mPrevCachedReferenceFrame;
nsDisplayLayerEventRegions* mPrevLayerEventRegions;
nsPoint mPrevCachedOffset;
bool mPrevIsAtRootOfPseudoStackingContext;
bool mPrevAncestorHasTouchEventHandler;
};
/**
@ -629,6 +657,7 @@ private:
nsIFrame* mReferenceFrame;
nsIFrame* mIgnoreScrollFrame;
nsDisplayLayerEventRegions* mLayerEventRegions;
PLArenaPool mPool;
nsCOMPtr<nsISelection> mBoundingSelection;
nsAutoTArray<PresShellState,8> mPresShellStates;
@ -665,6 +694,7 @@ private:
bool mIsCompositingCheap;
bool mContainsPluginItem;
bool mContainsBlendMode;
bool mAncestorHasTouchEventHandler;
};
class nsDisplayItem;
@ -2318,6 +2348,66 @@ public:
NS_DISPLAY_DECL_NAME("EventReceiver", TYPE_EVENT_RECEIVER)
};
/**
* A display item that tracks event-sensitive regions which will be set
* on the ContainerLayer that eventually contains this item.
*
* One of these is created for each stacking context and pseudo-stacking-context.
* It accumulates regions for event targets contributed by the border-boxes of
* frames in its (pseudo) stacking context. A nsDisplayLayerEventRegions
* eventually contributes its regions to the ThebesLayer it is placed in by
* FrameLayerBuilder. (We don't create a display item for every frame that
* could be an event target (i.e. almost all frames), because that would be
* high overhead.)
*
* We always make leaf layers other than ThebesLayers transparent to events.
* For example, an event targeting a canvas or video will actually target the
* background of that element, which is logically in the ThebesLayer behind the
* CanvasFrame or ImageFrame. We only need to create a
* nsDisplayLayerEventRegions when an element's background could be in front
* of a lower z-order element with its own layer.
*/
class nsDisplayLayerEventRegions MOZ_FINAL : public nsDisplayItem {
public:
nsDisplayLayerEventRegions(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
: nsDisplayItem(aBuilder, aFrame)
{
MOZ_COUNT_CTOR(nsDisplayEventReceiver);
AddFrame(aBuilder, aFrame);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayLayerEventRegions() {
MOZ_COUNT_DTOR(nsDisplayEventReceiver);
}
#endif
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE
{
*aSnap = false;
return mHitRegion.GetBounds().Union(mMaybeHitRegion.GetBounds());
}
NS_DISPLAY_DECL_NAME("LayerEventRegions", TYPE_LAYER_EVENT_REGIONS)
// Indicate that aFrame's border-box contributes to the event regions for
// this layer. aFrame must have the same reference frame as mFrame.
void AddFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
const nsRegion& HitRegion() { return mHitRegion; }
const nsRegion& MaybeHitRegion() { return mMaybeHitRegion; }
const nsRegion& DispatchToContentHitRegion() { return mDispatchToContentHitRegion; }
private:
// Relative to aFrame's reference frame.
// These are the points that are definitely in the hit region.
nsRegion mHitRegion;
// These are points that may or may not be in the hit region. Only main-thread
// event handling can tell for sure (e.g. because complex shapes are present).
nsRegion mMaybeHitRegion;
// These are points that need to be dispatched to the content thread for
// resolution. Always contained in the union of mHitRegion and mMaybeHitRegion.
nsRegion mDispatchToContentHitRegion;
};
/**
* A class that lets you wrap a display list as a display item.
*

View File

@ -77,6 +77,7 @@
#include "gfxASurface.h"
#include "nsRegion.h"
#include "nsIFrameInlines.h"
#include "nsEventListenerManager.h"
#include "mozilla/Preferences.h"
#include "mozilla/LookAndFeel.h"
@ -1789,6 +1790,23 @@ public:
}
};
static void
CheckForTouchEventHandler(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
{
nsIContent* content = aFrame->GetContent();
if (!content) {
return;
}
nsEventListenerManager* elm = nsContentUtils::GetExistingListenerManagerForNode(content);
if (!elm) {
return;
}
if (elm->HasListenersFor(nsGkAtoms::ontouchstart) ||
elm->HasListenersFor(nsGkAtoms::ontouchmove)) {
aBuilder->SetAncestorHasTouchEventHandler(true);
}
}
void
nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
@ -1878,6 +1896,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
nsDisplayListBuilder::AutoInTransformSetter
inTransformSetter(aBuilder, inTransform);
CheckForTouchEventHandler(aBuilder, this);
if (usingSVGEffects) {
dirtyRect =
@ -1898,6 +1917,12 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
aBuilder->MarkPreserve3DFramesForDisplayList(this, aDirtyRect);
}
if (aBuilder->IsBuildingLayerEventRegions()) {
nsDisplayLayerEventRegions* eventRegions =
new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this);
aBuilder->SetLayerEventRegions(eventRegions);
set.BorderBackground()->AppendNewToTop(eventRegions);
}
BuildDisplayList(aBuilder, dirtyRect, set);
}
@ -2187,6 +2212,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
nsDisplayListBuilder::AutoBuildingDisplayList
buildingForChild(aBuilder, child, pseudoStackingContext);
DisplayListClipState::AutoClipMultiple clipState(aBuilder);
CheckForTouchEventHandler(aBuilder, child);
if (savedOutOfFlowData) {
clipState.SetClipForContainingBlockDescendants(
@ -2231,6 +2257,10 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
// THIS IS THE COMMON CASE.
// Not a pseudo or real stacking context. Do the simple thing and
// return early.
nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions();
if (eventRegions) {
eventRegions->AddFrame(aBuilder, child);
}
child->BuildDisplayList(aBuilder, dirty, aLists);
aBuilder->DisplayCaret(child, dirty, aLists.Content());
#ifdef DEBUG
@ -2244,6 +2274,12 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
// stacking context's positioned descendant list, because they might be
// z-index:non-auto
nsDisplayListCollection pseudoStack;
if (aBuilder->IsBuildingLayerEventRegions()) {
nsDisplayLayerEventRegions* eventRegions =
new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this);
aBuilder->SetLayerEventRegions(eventRegions);
pseudoStack.BorderBackground()->AppendNewToTop(eventRegions);
}
child->BuildDisplayList(aBuilder, dirty, pseudoStack);
aBuilder->DisplayCaret(child, dirty, pseudoStack.Content());

View File

@ -401,6 +401,7 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
}
if (subdocRootFrame) {
aBuilder->SetAncestorHasTouchEventHandler(false);
subdocRootFrame->
BuildDisplayListForStackingContext(aBuilder, dirty, &childItems);
}