Fix scrollbars on subframes not directing APZ events to the correct scrollframe. (bug 1213324, r=tn)

This commit is contained in:
David Anderson 2015-10-20 02:39:34 -07:00
parent 3306b77a11
commit fb84bcaa55
5 changed files with 151 additions and 7 deletions

View File

@ -29,3 +29,5 @@ skip-if = toolkit != 'gonk'
skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
[test_scroll_inactive_bug1190112.html]
skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
[test_scroll_subframe_scrollbar.html]
skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet

View File

@ -0,0 +1,119 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test scrolling subframe scrollbars</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<style>
p {
width:200px;
height:200px;
border:solid 1px black;
}
</style>
</head>
<body>
<p id="subframe">
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p>
<script clss="testbody" type="text/javascript;version=1.7">
var DefaultEvent = {
deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0, deltaY: 1,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
};
var ScrollbarWidth = 0;
function test() {
var subframe = document.getElementById('subframe');
var oldClientWidth = subframe.clientWidth;
subframe.style.overflow = 'auto';
subframe.getBoundingClientRect();
waitForAllPaintsFlushed(function () {
ScrollbarWidth = oldClientWidth - subframe.clientWidth;
if (!ScrollbarWidth) {
// Probably we have overlay scrollbars - abort the test.
ok(true, "overlay scrollbars - skipping test");
SimpleTest.finish();
return;
}
ok(subframe.scrollHeight > subframe.clientHeight, "subframe should have scrollable content");
testScrolling(subframe);
});
}
function testScrolling(subframe) {
// Send a wheel event roughly to where we think the trackbar is. We pick a
// point at the bottom, in the middle of the trackbar, where the slider is
// unlikely to be (since it starts at the top).
var posX = subframe.clientWidth + (ScrollbarWidth / 2);
var posY = subframe.clientHeight - 20;
var oldScrollTop = subframe.scrollTop;
sendWheelAndPaint(subframe, posX, posY, DefaultEvent, function () {
ok(subframe.scrollTop > oldScrollTop, "subframe should have scrolled");
SimpleTest.finish();
});
}
window.onload = function() {
SpecialPowers.pushPrefEnv({
'set': [['general.smoothScroll', false],
['mousewheel.transaction.timeout', 0],
['mousewheel.transaction.ignoremovedelay', 0]]
}, function () {
SimpleTest.waitForFocus(test);
});
}
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>

View File

@ -624,6 +624,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID),
mCurrentScrollbarFlags(0),
mCurrentScrollbarWillHaveLayer(false),
mBuildCaret(aBuildCaret),
mIgnoreSuppression(false),
mHadToIgnoreSuppression(false),
@ -3214,7 +3215,15 @@ nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
} else {
mHitRegion.Or(mHitRegion, borderBox);
}
if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
if (aBuilder->IsBuildingNonLayerizedScrollbar() ||
aBuilder->GetAncestorHasApzAwareEventHandler())
{
// Scrollbars may be painted into a layer below the actual layer they will
// scroll, and therefore wheel events may be dispatched to the outer frame
// instead of the intended scrollframe. To address this, we force a d-t-c
// region on scrollbar frames that won't be placed in their own layer. See
// bug 1213324 for details.
mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, borderBox);
} else if (aFrame->GetType() == nsGkAtoms::objectFrame) {
// If the frame is a plugin frame and wants to handle wheel events as

View File

@ -312,6 +312,14 @@ public:
*aOutScrollbarTarget = mCurrentScrollbarTarget;
*aOutScrollbarFlags = mCurrentScrollbarFlags;
}
/**
* Returns true if building a scrollbar, and the scrollbar will not be
* layerized.
*/
bool IsBuildingNonLayerizedScrollbar() const {
return mCurrentScrollbarTarget != FrameMetrics::NULL_SCROLL_ID &&
!mCurrentScrollbarWillHaveLayer;
}
/**
* Calling this setter makes us include all out-of-flow descendant
* frames in the display list, wherever they may be positioned (even
@ -753,15 +761,17 @@ public:
class AutoCurrentScrollbarInfoSetter {
public:
AutoCurrentScrollbarInfoSetter(nsDisplayListBuilder* aBuilder, ViewID aScrollTargetID,
uint32_t aScrollbarFlags)
uint32_t aScrollbarFlags, bool aWillHaveLayer)
: mBuilder(aBuilder) {
aBuilder->mCurrentScrollbarTarget = aScrollTargetID;
aBuilder->mCurrentScrollbarFlags = aScrollbarFlags;
aBuilder->mCurrentScrollbarWillHaveLayer = aWillHaveLayer;
}
~AutoCurrentScrollbarInfoSetter() {
// No need to restore old values because scrollbars cannot be nested.
mBuilder->mCurrentScrollbarTarget = FrameMetrics::NULL_SCROLL_ID;
mBuilder->mCurrentScrollbarFlags = 0;
mBuilder->mCurrentScrollbarWillHaveLayer = false;
}
private:
nsDisplayListBuilder* mBuilder;
@ -1156,6 +1166,7 @@ private:
uint32_t mCurrentScrollbarFlags;
BlendModeSet mContainedBlendModes;
Preserves3DContext mPreserves3DCtx;
bool mCurrentScrollbarWillHaveLayer;
bool mBuildCaret;
bool mIgnoreSuppression;
bool mHadToIgnoreSuppression;

View File

@ -2699,17 +2699,20 @@ ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder,
nsDisplayListBuilder::AutoBuildingDisplayList
buildingForChild(aBuilder, scrollParts[i],
dirty + mOuter->GetOffsetTo(scrollParts[i]), true);
// Always create layers for overlay scrollbars so that we don't create a
// giant layer covering the whole scrollport if both scrollbars are visible.
bool isOverlayScrollbar = (flags != 0) && overlayScrollbars;
bool createLayer = aCreateLayer || isOverlayScrollbar;
nsDisplayListBuilder::AutoCurrentScrollbarInfoSetter
infoSetter(aBuilder, scrollTargetId, flags);
infoSetter(aBuilder, scrollTargetId, flags, createLayer);
nsDisplayListCollection partList;
mOuter->BuildDisplayListForChild(
aBuilder, scrollParts[i], dirty, partList,
nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
// Always create layers for overlay scrollbars so that we don't create a
// giant layer covering the whole scrollport if both scrollbars are visible.
bool isOverlayScrollbar = (flags != 0) && overlayScrollbars;
if (aCreateLayer || isOverlayScrollbar) {
if (createLayer) {
appendToTopFlags |= APPEND_OWN_LAYER;
}
if (aPositioned) {