mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 1486952 - Update overflow on the correct svg frame(s) when the viewBox changes. r=jwatt
Bug 828240 switched the children only transform on an outer svg from applying to each of the anonymous child's children to applying directly to the anonymous child instead. So now when the viewBox changes on an outer svg, we need to update (just) the overflow of the anonymous child. The children only transform on an inner svg still applies to the children of the inner svg, so we continue updating those children in that case. Hit testing uses overflows as part of the testing process, so was broken by the lack of overflow updates. Differential Revision: https://phabricator.services.mozilla.com/D5668 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
1bc409189c
commit
4c5f52fbca
@ -46,6 +46,7 @@ support-files =
|
||||
[test_getElementById.xhtml]
|
||||
[test_getSubStringLength.xhtml]
|
||||
[test_getTotalLength.xhtml]
|
||||
[test_hit-testing-and-viewbox.xhtml]
|
||||
[test_lang.xhtml]
|
||||
skip-if = true # disabled-for-intermittent-failures--bug-701060
|
||||
[test_length.xhtml]
|
||||
|
82
dom/svg/test/test_hit-testing-and-viewbox.xhtml
Normal file
82
dom/svg/test/test_hit-testing-and-viewbox.xhtml
Normal file
@ -0,0 +1,82 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1486952
|
||||
-->
|
||||
<head>
|
||||
<title>Test that hit-testing works after a viewBox update</title>
|
||||
|
||||
<style>
|
||||
:hover { fill: lime; }
|
||||
</style>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
||||
<body onload="run()">
|
||||
<script class="testbody" type="text/javascript">
|
||||
<![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function run()
|
||||
{
|
||||
const div = $('div');
|
||||
const offsetX = div.offsetLeft;
|
||||
const offsetY = div.offsetTop;
|
||||
const outerRect = $('outerRect');
|
||||
const innerRect = $('innerRect');
|
||||
const outerSVG = $('outerSVG');
|
||||
const innerSVG = $('innerSVG');
|
||||
let got;
|
||||
|
||||
// Update the inner SVG viewBox to "move" the inner rectangle off its current
|
||||
// position on screen:
|
||||
innerSVG.setAttribute('viewBox', '-25 0 50 50');
|
||||
got = document.elementFromPoint(offsetX + 150, offsetY + 25);
|
||||
is(got, innerRect, 'Should hit inner rectangle (1)');
|
||||
|
||||
// Update the inner SVG viewBox again. (At the time of writing, a reflow is
|
||||
// triggered the first time you change viewBox on an inner svg, so the
|
||||
// previous test is expected to always pass. This next test will fail if we're
|
||||
// updating overflows on the inner svg frame instead of its children).
|
||||
innerSVG.setAttribute('viewBox', '0 -25 50 50');
|
||||
got = document.elementFromPoint(offsetX + 100, offsetY + 75);
|
||||
is(got, innerRect, 'Should hit inner rectangle (2)');
|
||||
|
||||
// Now update the outer SVG viewBox and confirm that both rectangles are
|
||||
// registered. (Note that in this case the overflow rectangle of the inner
|
||||
// svg is the inner svg's viewport, so be sure to "move" the outer svg so that
|
||||
// the "new" outer rectangle and inner svg are off the current outerRect
|
||||
// union inner svg viewport - hit testing still works in that union.)
|
||||
outerSVG.setAttribute('viewBox', '-200 0 400 100');
|
||||
// Outer:
|
||||
got = document.elementFromPoint(offsetX + 250, offsetY + 50);
|
||||
is(got, outerRect, 'Should hit outer rectangle');
|
||||
// Inner:
|
||||
got = document.elementFromPoint(offsetX + 300, offsetY + 75);
|
||||
is(got, innerRect, 'Should hit inner rectangle (3)');
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1486952">Mozilla Bug 1486952</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
|
||||
<div width="100%" height="1" id="div"></div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="outerSVG" width="400" height="100"
|
||||
viewBox="0 0 400 100">
|
||||
<rect x="25" y="25" width="50" height="50" fill="red" id="outerRect" />
|
||||
<svg x="75" width="100" height="100" viewBox="0 0 50 50" id="innerSVG">
|
||||
<rect width="25" height="25" fill="red" id="innerRect" />
|
||||
</svg>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -667,11 +667,9 @@ static void SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
|
||||
static void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
|
||||
|
||||
/**
|
||||
* To handle nsChangeHint_ChildrenOnlyTransform we must iterate over the child
|
||||
* frames of the SVG frame concerned. This helper function is used to find that
|
||||
* SVG frame when we encounter nsChangeHint_ChildrenOnlyTransform to ensure
|
||||
* that we iterate over the intended children, since sometimes we end up
|
||||
* handling that hint while processing hints for one of the SVG frame's
|
||||
* This helper function is used to find the correct SVG frame to target when we
|
||||
* encounter nsChangeHint_ChildrenOnlyTransform; needed since sometimes we end
|
||||
* up handling that hint while processing hints for one of the SVG frame's
|
||||
* ancestor frames.
|
||||
*
|
||||
* The reason that we sometimes end up trying to process the hint for an
|
||||
@ -681,8 +679,7 @@ static void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
|
||||
* on what nsCSSRendering::FindBackground returns, since the background style
|
||||
* may have been propagated up to an ancestor frame. Processing hints using an
|
||||
* ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
|
||||
* a special case since it is intended to update the children of a specific
|
||||
* frame.
|
||||
* a special case since it is intended to update a specific frame.
|
||||
*/
|
||||
static nsIFrame*
|
||||
GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame)
|
||||
@ -1688,30 +1685,47 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
|
||||
nsChangeHint_UpdatePostTransformOverflow);
|
||||
}
|
||||
if (hint & nsChangeHint_ChildrenOnlyTransform) {
|
||||
// The overflow areas of the child frames need to be updated:
|
||||
// We need to update overflows. The correct frame(s) to update depends
|
||||
// on whether the ChangeHint came from an outer or an inner svg.
|
||||
nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
|
||||
nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild();
|
||||
NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
|
||||
"SVG frames should not have continuations "
|
||||
"or ib-split siblings");
|
||||
NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
|
||||
"SVG frames should not have continuations "
|
||||
"or ib-split siblings");
|
||||
for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
|
||||
MOZ_ASSERT(childFrame->IsFrameOfType(nsIFrame::eSVG),
|
||||
"Not expecting non-SVG children");
|
||||
// If |childFrame| is dirty or has dirty children, we don't bother
|
||||
if (hintFrame->IsSVGOuterSVGAnonChildFrame()) {
|
||||
// The children only transform of an outer svg frame is applied to
|
||||
// the outer svg's anonymous child frame (instead of to the
|
||||
// anonymous child's children).
|
||||
|
||||
// If |hintFrame| is dirty or has dirty children, we don't bother
|
||||
// updating overflows since that will happen when it's reflowed.
|
||||
if (!(childFrame->GetStateBits() &
|
||||
if (!(hintFrame->GetStateBits() &
|
||||
(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
|
||||
mOverflowChangedTracker.AddFrame(childFrame,
|
||||
OverflowChangedTracker::CHILDREN_CHANGED);
|
||||
mOverflowChangedTracker.AddFrame(hintFrame,
|
||||
OverflowChangedTracker::CHILDREN_CHANGED);
|
||||
}
|
||||
} else {
|
||||
// The children only transform is applied to the child frames of an
|
||||
// inner svg frame, so update the child overflows.
|
||||
nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild();
|
||||
for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
|
||||
MOZ_ASSERT(childFrame->IsFrameOfType(nsIFrame::eSVG),
|
||||
"Not expecting non-SVG children");
|
||||
// If |childFrame| is dirty or has dirty children, we don't bother
|
||||
// updating overflows since that will happen when it's reflowed.
|
||||
if (!(childFrame->GetStateBits() &
|
||||
(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
|
||||
mOverflowChangedTracker.AddFrame(childFrame,
|
||||
OverflowChangedTracker::CHILDREN_CHANGED);
|
||||
}
|
||||
NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
|
||||
"SVG frames should not have continuations "
|
||||
"or ib-split siblings");
|
||||
NS_ASSERTION(childFrame->GetParent() == hintFrame,
|
||||
"SVG child frame not expected to have different parent");
|
||||
}
|
||||
NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
|
||||
"SVG frames should not have continuations "
|
||||
"or ib-split siblings");
|
||||
NS_ASSERTION(childFrame->GetParent() == hintFrame,
|
||||
"SVG child frame not expected to have different parent");
|
||||
}
|
||||
}
|
||||
// If |frame| is dirty or has dirty children, we don't bother updating
|
||||
|
@ -111,8 +111,8 @@ enum nsChangeHint : uint32_t {
|
||||
nsChangeHint_UpdateParentOverflow = 1 << 14,
|
||||
|
||||
/**
|
||||
* The children-only transform of an SVG frame changed, requiring the
|
||||
* overflow rects of the frame's immediate children to be updated.
|
||||
* The children-only transform of an SVG frame changed, requiring overflows to
|
||||
* be updated.
|
||||
*/
|
||||
nsChangeHint_ChildrenOnlyTransform = 1 << 15,
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user