Bug 1465935 - Fix hit-testing for fixed-pos items inside iframes. r?mstange
Without this patch, the scrollId for display items inside a fixed-pos
item end as the ASR of the item. In the case of fixed-pos items that are
inside iframes, the ASR is the outer document's root scrollframe. This
means that e.g. wheel-scrolling while over a fixed-pos item inside an
iframe ends up scrolling the outer document's root scrollframe instead
of the iframe's root scrollframe.
In the non-WR codepath, there some APZ machinery that walks up in the
HitTestingTreeNode tree from the hit result, looking to see if that node
has a fixed-pos ancestor, and if so, uses the fixed-pos item's target
APZ as the real hit result. This machinery doesn't exist in WR, because
we don't use the HitTestingTreeNode tree for hit-testing in APZ.
Instead, we need to make sure that the item tag for those display items
already has the appropriate scrollid set.
This patch accomplishes this by introducing a new RAII class that is
pushed into the wr::DisplayListBuilder while we are building display
items inside a nsDisplayFixedPosition, and allows the desired scroll id to
be set on the hit-testing display items.
This behaviour is exercised by test_group_wheelevents, which can now be
enabled with this fix.
MozReview-Commit-ID: L2erPVzJeql
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -67,17 +67,17 @@
skip-if = webrender # bug 1424752
[test_group_mouseevents.html]
skip-if = (toolkit == 'android') # mouse events not supported on mobile
[test_group_pointerevents.html]
skip-if = os == 'win' && os_version == '10.0' # Bug 1404836
[test_group_touchevents.html]
skip-if = webrender # bug 1424752
[test_group_wheelevents.html]
- skip-if = (toolkit == 'android') || webrender # wheel events not supported on mobile; bug 1429521 for webrender
+ skip-if = (toolkit == 'android') # wheel events not supported on mobile
[test_group_zoom.html]
skip-if = (toolkit != 'android') # only android supports zoom
[test_interrupted_reflow.html]
skip-if = webrender # bug 1429521 for webrender
[test_group_keyboard.html]
[test_layerization.html]
skip-if = (os == 'android') || webrender # wheel events not supported on mobile; bug 1424752 for webrender
[test_scroll_inactive_bug1190112.html]
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -715,16 +715,17 @@ WebRenderAPI::RunOnRenderThread(UniquePt
{
auto event = reinterpret_cast<uintptr_t>(aEvent.release());
wr_api_send_external_event(mDocHandle, event);
}
DisplayListBuilder::DisplayListBuilder(PipelineId aId,
const wr::LayoutSize& aContentSize,
size_t aCapacity)
+ : mActiveFixedPosTracker(nullptr)
{
MOZ_COUNT_CTOR(DisplayListBuilder);
mWrState = wr_state_new(aId, aContentSize, aCapacity);
}
DisplayListBuilder::~DisplayListBuilder()
{
MOZ_COUNT_DTOR(DisplayListBuilder);
@@ -1209,25 +1210,56 @@ DisplayListBuilder::PushBoxShadow(const
const wr::BoxShadowClipMode& aClipMode)
{
wr_dp_push_box_shadow(mWrState, aRect, aClip, aIsBackfaceVisible,
aBoxBounds, aOffset, aColor,
aBlurRadius, aSpreadRadius, aBorderRadius,
aClipMode);
}
+Maybe<layers::FrameMetrics::ViewID>
+DisplayListBuilder::GetContainingFixedPosScrollTarget(const ActiveScrolledRoot* aAsr)
+{
+ return mActiveFixedPosTracker
+ ? mActiveFixedPosTracker->GetScrollTargetForASR(aAsr)
+ : Nothing();
+}
+
void
DisplayListBuilder::SetHitTestInfo(const layers::FrameMetrics::ViewID& aScrollId,
gfx::CompositorHitTestInfo aHitInfo)
{
static_assert(sizeof(gfx::CompositorHitTestInfo) == sizeof(uint16_t),
"CompositorHitTestInfo should be u16-sized");
wr_set_item_tag(mWrState, aScrollId, static_cast<uint16_t>(aHitInfo));
}
void
DisplayListBuilder::ClearHitTestInfo()
{
wr_clear_item_tag(mWrState);
}
+DisplayListBuilder::FixedPosScrollTargetTracker::FixedPosScrollTargetTracker(
+ DisplayListBuilder& aBuilder,
+ const ActiveScrolledRoot* aAsr,
+ layers::FrameMetrics::ViewID aScrollId)
+ : mParentTracker(aBuilder.mActiveFixedPosTracker)
+ , mBuilder(aBuilder)
+ , mAsr(aAsr)
+ , mScrollId(aScrollId)
+{
+ aBuilder.mActiveFixedPosTracker = this;
+}
+
+DisplayListBuilder::FixedPosScrollTargetTracker::~FixedPosScrollTargetTracker()
+{
+ mBuilder.mActiveFixedPosTracker = mParentTracker;
+}
+
+Maybe<layers::FrameMetrics::ViewID>
+DisplayListBuilder::FixedPosScrollTargetTracker::GetScrollTargetForASR(const ActiveScrolledRoot* aAsr)
+{
+ return aAsr == mAsr ? Some(mScrollId) : Nothing();
+}
+
} // namespace wr
} // namespace mozilla
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -18,16 +18,18 @@
#include "mozilla/webrender/webrender_ffi.h"
#include "mozilla/webrender/WebRenderTypes.h"
#include "FrameMetrics.h"
#include "GLTypes.h"
#include "Units.h"
namespace mozilla {
+struct ActiveScrolledRoot;
+
namespace widget {
class CompositorWidget;
}
namespace layers {
class CompositorBridgeParent;
class WebRenderBridgeParent;
}
@@ -461,34 +463,59 @@ public:
const wr::LayoutRect& aBoxBounds,
const wr::LayoutVector2D& aOffset,
const wr::ColorF& aColor,
const float& aBlurRadius,
const float& aSpreadRadius,
const wr::BorderRadius& aBorderRadius,
const wr::BoxShadowClipMode& aClipMode);
+ // Checks to see if the innermost enclosing fixed pos item has the same
+ // ASR. If so, it returns the scroll target for that fixed-pos item.
+ // Otherwise, it returns Nothing().
+ Maybe<layers::FrameMetrics::ViewID> GetContainingFixedPosScrollTarget(const ActiveScrolledRoot* aAsr);
+
// Set the hit-test info to be used for all display items until the next call
// to SetHitTestInfo or ClearHitTestInfo.
void SetHitTestInfo(const layers::FrameMetrics::ViewID& aScrollId,
gfx::CompositorHitTestInfo aHitInfo);
// Clears the hit-test info so that subsequent display items will not have it.
void ClearHitTestInfo();
// Try to avoid using this when possible.
wr::WrState* Raw() { return mWrState; }
+ // A chain of RAII objects, each holding a (ASR, ViewID) tuple of data. The
+ // topmost object is pointed to by the mActiveFixedPosTracker pointer in
+ // the wr::DisplayListBuilder.
+ class MOZ_RAII FixedPosScrollTargetTracker {
+ public:
+ FixedPosScrollTargetTracker(DisplayListBuilder& aBuilder,
+ const ActiveScrolledRoot* aAsr,
+ layers::FrameMetrics::ViewID aScrollId);
+ ~FixedPosScrollTargetTracker();
+ Maybe<layers::FrameMetrics::ViewID> GetScrollTargetForASR(const ActiveScrolledRoot* aAsr);
+
+ private:
+ FixedPosScrollTargetTracker* mParentTracker;
+ DisplayListBuilder& mBuilder;
+ const ActiveScrolledRoot* mAsr;
+ layers::FrameMetrics::ViewID mScrollId;
+ };
+
protected:
wr::WrState* mWrState;
// Track each scroll id that we encountered. We use this structure to
// ensure that we don't define a particular scroll layer multiple times,
// as that results in undefined behaviour in WR.
std::unordered_map<layers::FrameMetrics::ViewID, wr::WrClipId> mScrollIds;
+ FixedPosScrollTargetTracker* mActiveFixedPosTracker;
+
friend class WebRenderAPI;
};
Maybe<wr::ImageFormat>
SurfaceFormatToImageFormat(gfx::SurfaceFormat aFormat);
} // namespace wr
} // namespace mozilla
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -5100,17 +5100,23 @@ nsDisplayCompositorHitTestInfo::CreateWe
// XXX: eventually this scrollId computation and the SetHitTestInfo
// call will get moved out into the WR display item iteration code so that
// we don't need to do it as often, and so that we can do it for other
// display item types as well (reducing the need for as many instances of
// this display item).
FrameMetrics::ViewID scrollId = mScrollTarget.valueOrFrom(
[&]() -> FrameMetrics::ViewID {
- if (const ActiveScrolledRoot* asr = GetActiveScrolledRoot()) {
+ const ActiveScrolledRoot* asr = GetActiveScrolledRoot();
+ Maybe<FrameMetrics::ViewID> fixedTarget =
+ aBuilder.GetContainingFixedPosScrollTarget(asr);
+ if (fixedTarget) {
+ return *fixedTarget;
+ }
+ if (asr) {
return asr->GetViewId();
}
return FrameMetrics::NULL_SCROLL_ID;
});
// Insert a transparent rectangle with the hit-test info
aBuilder.SetHitTestInfo(scrollId, mHitTestInfo);
@@ -7417,16 +7423,35 @@ nsDisplayFixedPosition::BuildLayer(nsDis
nsLayoutUtils::SetFixedPositionLayerData(layer,
viewportFrame, anchorRect, fixedFrame, presContext, aContainerParameters);
return layer.forget();
}
bool
+nsDisplayFixedPosition::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+ mozilla::wr::IpcResourceUpdateQueue& aResources,
+ const StackingContextHelper& aSc,
+ mozilla::layers::WebRenderLayerManager* aManager,
+ nsDisplayListBuilder* aDisplayListBuilder)
+{
+ // We install this RAII scrolltarget tracker so that any
+ // nsDisplayCompositorHitTestInfo items inside this fixed-pos item (and that
+ // share the same ASR as this item) use the correct scroll target. That way
+ // attempts to scroll on those items will scroll the root scroll frame.
+ mozilla::wr::DisplayListBuilder::FixedPosScrollTargetTracker tracker(
+ aBuilder,
+ GetActiveScrolledRoot(),
+ nsLayoutUtils::ScrollIdForRootScrollFrame(Frame()->PresContext()));
+ return nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, aSc,
+ aManager, aDisplayListBuilder);
+}
+
+bool
nsDisplayFixedPosition::UpdateScrollData(mozilla::layers::WebRenderScrollData* aData,
mozilla::layers::WebRenderLayerScrollData* aLayerData)
{
if (aLayerData) {
FrameMetrics::ViewID id = nsLayoutUtils::ScrollIdForRootScrollFrame(
Frame()->PresContext());
aLayerData->SetFixedPositionScrollContainerId(id);
}
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -6004,16 +6004,21 @@ public:
virtual uint32_t GetPerFrameKey() const override {
return (mIndex << TYPE_BITS) | nsDisplayItem::GetPerFrameKey();
}
AnimatedGeometryRoot* AnimatedGeometryRootForScrollMetadata() const override {
return mAnimatedGeometryRootForScrollMetadata;
}
+ virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+ mozilla::wr::IpcResourceUpdateQueue& aResources,
+ const StackingContextHelper& aSc,
+ mozilla::layers::WebRenderLayerManager* aManager,
+ nsDisplayListBuilder* aDisplayListBuilder) override;
virtual bool UpdateScrollData(mozilla::layers::WebRenderScrollData* aData,
mozilla::layers::WebRenderLayerScrollData* aLayerData) override;
protected:
// For background-attachment:fixed
nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList, uint32_t aIndex);
void Init(nsDisplayListBuilder* aBuilder);