зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1555758 - Fallback to GetComputedStyleNoFlush for BuildPath r=longsonr,emilio
It turns out the `BuildPath()` method will still be exposed by DOM API via `getTotalLength()`. So we need to fallback to `GetComputedStyleNoFlush()` to handle display:none problem, and if the element doesn't even belong to a document, we further fallback to 0. `BuildPath()` being affected also means `GetStrokeWidth()`, etc. will also be indirectly exposed to DOM API in some obscure cases. Let's add a utility to handle the fallback. Differential Revision: https://phabricator.services.mozilla.com/D33247 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
62aa7c0ec3
Коммит
a524e7ef15
|
@ -127,9 +127,8 @@ bool SVGCircleElement::GetGeometryBounds(
|
|||
|
||||
already_AddRefed<Path> SVGCircleElement::BuildPath(PathBuilder* aBuilder) {
|
||||
float x, y, r;
|
||||
MOZ_ASSERT(GetPrimaryFrame());
|
||||
SVGGeometryProperty::ResolveAll<SVGT::Cx, SVGT::Cy, SVGT::R>(this, &x, &y,
|
||||
&r);
|
||||
SVGGeometryProperty::ResolveAllAllowFallback<SVGT::Cx, SVGT::Cy, SVGT::R>(
|
||||
this, &x, &y, &r);
|
||||
|
||||
if (r <= 0.0f) {
|
||||
return nullptr;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "nsWhitespaceTokenizer.h"
|
||||
#include "SVGAnimationElement.h"
|
||||
#include "SVGAnimatedPreserveAspectRatio.h"
|
||||
#include "SVGGeometryProperty.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
|
@ -255,93 +256,97 @@ static DashState GetStrokeDashData(
|
|||
|
||||
void SVGContentUtils::GetStrokeOptions(AutoStrokeOptions* aStrokeOptions,
|
||||
SVGElement* aElement,
|
||||
ComputedStyle* aComputedStyle,
|
||||
const ComputedStyle* aComputedStyle,
|
||||
SVGContextPaint* aContextPaint,
|
||||
StrokeOptionFlags aFlags) {
|
||||
ComputedStyle* computedStyle;
|
||||
auto doCompute = [&](const ComputedStyle* computedStyle) {
|
||||
const nsStyleSVG* styleSVG = computedStyle->StyleSVG();
|
||||
|
||||
bool checkedDashAndStrokeIsDashed = false;
|
||||
if (aFlags != eIgnoreStrokeDashing) {
|
||||
DashState dashState =
|
||||
GetStrokeDashData(aStrokeOptions, aElement, styleSVG, aContextPaint);
|
||||
|
||||
if (dashState == eNoStroke) {
|
||||
// Hopefully this will shortcircuit any stroke operations:
|
||||
aStrokeOptions->mLineWidth = 0;
|
||||
return;
|
||||
}
|
||||
if (dashState == eContinuousStroke && aStrokeOptions->mDashPattern) {
|
||||
// Prevent our caller from wasting time looking at a pattern without
|
||||
// gaps:
|
||||
aStrokeOptions->DiscardDashPattern();
|
||||
}
|
||||
checkedDashAndStrokeIsDashed = (dashState == eDashedStroke);
|
||||
}
|
||||
|
||||
aStrokeOptions->mLineWidth =
|
||||
GetStrokeWidth(aElement, computedStyle, aContextPaint);
|
||||
|
||||
aStrokeOptions->mMiterLimit = Float(styleSVG->mStrokeMiterlimit);
|
||||
|
||||
switch (styleSVG->mStrokeLinejoin) {
|
||||
case NS_STYLE_STROKE_LINEJOIN_MITER:
|
||||
aStrokeOptions->mLineJoin = JoinStyle::MITER_OR_BEVEL;
|
||||
break;
|
||||
case NS_STYLE_STROKE_LINEJOIN_ROUND:
|
||||
aStrokeOptions->mLineJoin = JoinStyle::ROUND;
|
||||
break;
|
||||
case NS_STYLE_STROKE_LINEJOIN_BEVEL:
|
||||
aStrokeOptions->mLineJoin = JoinStyle::BEVEL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ShapeTypeHasNoCorners(aElement) && !checkedDashAndStrokeIsDashed) {
|
||||
// Note: if aFlags == eIgnoreStrokeDashing then we may be returning the
|
||||
// wrong linecap value here, since the actual linecap used on render in
|
||||
// this case depends on whether the stroke is dashed or not.
|
||||
aStrokeOptions->mLineCap = CapStyle::BUTT;
|
||||
} else {
|
||||
switch (styleSVG->mStrokeLinecap) {
|
||||
case NS_STYLE_STROKE_LINECAP_BUTT:
|
||||
aStrokeOptions->mLineCap = CapStyle::BUTT;
|
||||
break;
|
||||
case NS_STYLE_STROKE_LINECAP_ROUND:
|
||||
aStrokeOptions->mLineCap = CapStyle::ROUND;
|
||||
break;
|
||||
case NS_STYLE_STROKE_LINECAP_SQUARE:
|
||||
aStrokeOptions->mLineCap = CapStyle::SQUARE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (aComputedStyle) {
|
||||
computedStyle = aComputedStyle;
|
||||
} else if (auto* f = aElement->GetPrimaryFrame()) {
|
||||
computedStyle = f->Style();
|
||||
doCompute(aComputedStyle);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
const nsStyleSVG* styleSVG = computedStyle->StyleSVG();
|
||||
|
||||
bool checkedDashAndStrokeIsDashed = false;
|
||||
if (aFlags != eIgnoreStrokeDashing) {
|
||||
DashState dashState =
|
||||
GetStrokeDashData(aStrokeOptions, aElement, styleSVG, aContextPaint);
|
||||
|
||||
if (dashState == eNoStroke) {
|
||||
// Hopefully this will shortcircuit any stroke operations:
|
||||
aStrokeOptions->mLineWidth = 0;
|
||||
return;
|
||||
}
|
||||
if (dashState == eContinuousStroke && aStrokeOptions->mDashPattern) {
|
||||
// Prevent our caller from wasting time looking at a pattern without gaps:
|
||||
aStrokeOptions->DiscardDashPattern();
|
||||
}
|
||||
checkedDashAndStrokeIsDashed = (dashState == eDashedStroke);
|
||||
}
|
||||
|
||||
aStrokeOptions->mLineWidth =
|
||||
GetStrokeWidth(aElement, computedStyle, aContextPaint);
|
||||
|
||||
aStrokeOptions->mMiterLimit = Float(styleSVG->mStrokeMiterlimit);
|
||||
|
||||
switch (styleSVG->mStrokeLinejoin) {
|
||||
case NS_STYLE_STROKE_LINEJOIN_MITER:
|
||||
aStrokeOptions->mLineJoin = JoinStyle::MITER_OR_BEVEL;
|
||||
break;
|
||||
case NS_STYLE_STROKE_LINEJOIN_ROUND:
|
||||
aStrokeOptions->mLineJoin = JoinStyle::ROUND;
|
||||
break;
|
||||
case NS_STYLE_STROKE_LINEJOIN_BEVEL:
|
||||
aStrokeOptions->mLineJoin = JoinStyle::BEVEL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ShapeTypeHasNoCorners(aElement) && !checkedDashAndStrokeIsDashed) {
|
||||
// Note: if aFlags == eIgnoreStrokeDashing then we may be returning the
|
||||
// wrong linecap value here, since the actual linecap used on render in this
|
||||
// case depends on whether the stroke is dashed or not.
|
||||
aStrokeOptions->mLineCap = CapStyle::BUTT;
|
||||
} else {
|
||||
switch (styleSVG->mStrokeLinecap) {
|
||||
case NS_STYLE_STROKE_LINECAP_BUTT:
|
||||
aStrokeOptions->mLineCap = CapStyle::BUTT;
|
||||
break;
|
||||
case NS_STYLE_STROKE_LINECAP_ROUND:
|
||||
aStrokeOptions->mLineCap = CapStyle::ROUND;
|
||||
break;
|
||||
case NS_STYLE_STROKE_LINECAP_SQUARE:
|
||||
aStrokeOptions->mLineCap = CapStyle::SQUARE;
|
||||
break;
|
||||
}
|
||||
SVGGeometryProperty::DoForComputedStyle(aElement, doCompute);
|
||||
}
|
||||
}
|
||||
|
||||
Float SVGContentUtils::GetStrokeWidth(SVGElement* aElement,
|
||||
ComputedStyle* aComputedStyle,
|
||||
const ComputedStyle* aComputedStyle,
|
||||
SVGContextPaint* aContextPaint) {
|
||||
ComputedStyle* computedStyle;
|
||||
Float res = 0.0;
|
||||
|
||||
auto doCompute = [&](ComputedStyle const* computedStyle) {
|
||||
const nsStyleSVG* styleSVG = computedStyle->StyleSVG();
|
||||
|
||||
if (aContextPaint && styleSVG->StrokeWidthFromObject()) {
|
||||
res = aContextPaint->GetStrokeWidth();
|
||||
return;
|
||||
}
|
||||
|
||||
res = SVGContentUtils::CoordToFloat(aElement, styleSVG->mStrokeWidth);
|
||||
};
|
||||
|
||||
if (aComputedStyle) {
|
||||
computedStyle = aComputedStyle;
|
||||
} else if (auto* f = aElement->GetPrimaryFrame()) {
|
||||
computedStyle = f->Style();
|
||||
doCompute(aComputedStyle);
|
||||
} else {
|
||||
return 0.0f;
|
||||
SVGGeometryProperty::DoForComputedStyle(aElement, doCompute);
|
||||
}
|
||||
|
||||
const nsStyleSVG* styleSVG = computedStyle->StyleSVG();
|
||||
|
||||
if (aContextPaint && styleSVG->StrokeWidthFromObject()) {
|
||||
return aContextPaint->GetStrokeWidth();
|
||||
}
|
||||
|
||||
return SVGContentUtils::CoordToFloat(aElement, styleSVG->mStrokeWidth);
|
||||
return res;
|
||||
}
|
||||
|
||||
float SVGContentUtils::GetFontSize(Element* aElement) {
|
||||
|
|
|
@ -151,7 +151,7 @@ class SVGContentUtils {
|
|||
*/
|
||||
static void GetStrokeOptions(AutoStrokeOptions* aStrokeOptions,
|
||||
dom::SVGElement* aElement,
|
||||
ComputedStyle* aComputedStyle,
|
||||
const ComputedStyle* aComputedStyle,
|
||||
mozilla::SVGContextPaint* aContextPaint,
|
||||
StrokeOptionFlags aFlags = eAllStrokeOptions);
|
||||
|
||||
|
@ -165,7 +165,7 @@ class SVGContentUtils {
|
|||
* "0", respectively.
|
||||
*/
|
||||
static Float GetStrokeWidth(dom::SVGElement* aElement,
|
||||
ComputedStyle* aComputedStyle,
|
||||
const ComputedStyle* aComputedStyle,
|
||||
mozilla::SVGContextPaint* aContextPaint);
|
||||
|
||||
/*
|
||||
|
|
|
@ -139,9 +139,9 @@ bool SVGEllipseElement::GetGeometryBounds(
|
|||
already_AddRefed<Path> SVGEllipseElement::BuildPath(PathBuilder* aBuilder) {
|
||||
float x, y, rx, ry;
|
||||
|
||||
MOZ_ASSERT(GetPrimaryFrame());
|
||||
SVGGeometryProperty::ResolveAll<SVGT::Cx, SVGT::Cy, SVGT::Rx, SVGT::Ry>(
|
||||
this, &x, &y, &rx, &ry);
|
||||
SVGGeometryProperty::ResolveAllAllowFallback<SVGT::Cx, SVGT::Cy, SVGT::Rx,
|
||||
SVGT::Ry>(this, &x, &y, &rx,
|
||||
&ry);
|
||||
|
||||
if (rx <= 0.0f || ry <= 0.0f) {
|
||||
return nullptr;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "SVGAnimatedLength.h"
|
||||
#include "SVGCircleElement.h"
|
||||
#include "SVGEllipseElement.h"
|
||||
#include "SVGGeometryProperty.h"
|
||||
#include "SVGRectElement.h"
|
||||
#include "mozilla/dom/SVGLengthBinding.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
@ -133,15 +134,19 @@ FillRule SVGGeometryElement::GetFillRule() {
|
|||
FillRule fillRule =
|
||||
FillRule::FILL_WINDING; // Equivalent to StyleFillRule::Nonzero
|
||||
|
||||
if (auto* f = GetPrimaryFrame()) {
|
||||
MOZ_ASSERT(f->StyleSVG()->mFillRule == StyleFillRule::Nonzero ||
|
||||
f->StyleSVG()->mFillRule == StyleFillRule::Evenodd);
|
||||
bool res = SVGGeometryProperty::DoForComputedStyle(
|
||||
this, [&](const ComputedStyle* s) {
|
||||
const auto* styleSVG = s->StyleSVG();
|
||||
|
||||
if (f->StyleSVG()->mFillRule == StyleFillRule::Evenodd) {
|
||||
fillRule = FillRule::FILL_EVEN_ODD;
|
||||
}
|
||||
} else {
|
||||
// ReportToConsole
|
||||
MOZ_ASSERT(styleSVG->mFillRule == StyleFillRule::Nonzero ||
|
||||
styleSVG->mFillRule == StyleFillRule::Evenodd);
|
||||
|
||||
if (styleSVG->mFillRule == StyleFillRule::Evenodd) {
|
||||
fillRule = FillRule::FILL_EVEN_ODD;
|
||||
}
|
||||
});
|
||||
|
||||
if (!res) {
|
||||
NS_WARNING("Couldn't get ComputedStyle for content in GetFillRule");
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/dom/SVGElement.h"
|
||||
#include "ComputedStyle.h"
|
||||
#include "SVGAnimatedLength.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsSVGImageFrame.h"
|
||||
|
@ -78,6 +79,7 @@ struct Ry {
|
|||
namespace details {
|
||||
template <class T>
|
||||
using AlwaysFloat = float;
|
||||
using dummy = int[];
|
||||
|
||||
using CtxDirectionType = decltype(SVGContentUtils::X);
|
||||
|
||||
|
@ -187,6 +189,25 @@ float ResolveWith(const ComputedStyle& aStyle, const SVGElement* aElement) {
|
|||
typename Tag::ResolverType{});
|
||||
}
|
||||
|
||||
template <class Func>
|
||||
bool DoForComputedStyle(SVGElement* aElement, Func aFunc) {
|
||||
if (const nsIFrame* f = aElement->GetPrimaryFrame()) {
|
||||
aFunc(f->Style());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (RefPtr<ComputedStyle> computedStyle =
|
||||
nsComputedDOMStyle::GetComputedStyleNoFlush(aElement, nullptr)) {
|
||||
aFunc(computedStyle.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define SVGGEOMETRYPROPERTY_EVAL_ALL(expr) \
|
||||
(void)details::dummy { 0, (static_cast<void>(expr), 0)... }
|
||||
|
||||
// To add support for new properties, or to handle special cases for
|
||||
// existing properties, you can add a new tag in |Tags| and |ResolverTypes|
|
||||
// namespace, then implement the behavior in |details::ResolveImpl|.
|
||||
|
@ -194,13 +215,30 @@ template <class... Tags>
|
|||
bool ResolveAll(const SVGElement* aElement,
|
||||
details::AlwaysFloat<Tags>*... aRes) {
|
||||
if (nsIFrame const* f = aElement->GetPrimaryFrame()) {
|
||||
using dummy = int[];
|
||||
(void)dummy{0, (*aRes = ResolveWith<Tags>(*f->Style(), aElement), 0)...};
|
||||
SVGGEOMETRYPROPERTY_EVAL_ALL(*aRes =
|
||||
ResolveWith<Tags>(*f->Style(), aElement));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class... Tags>
|
||||
bool ResolveAllAllowFallback(SVGElement* aElement,
|
||||
details::AlwaysFloat<Tags>*... aRes) {
|
||||
bool res = DoForComputedStyle(aElement, [&](auto const* style) {
|
||||
SVGGEOMETRYPROPERTY_EVAL_ALL(*aRes = ResolveWith<Tags>(*style, aElement));
|
||||
});
|
||||
|
||||
if (res) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SVGGEOMETRYPROPERTY_EVAL_ALL(*aRes = 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
#undef SVGGEOMETRYPROPERTY_EVAL_ALL
|
||||
|
||||
nsCSSUnit SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit);
|
||||
nsCSSPropertyID AttrEnumToCSSPropId(const SVGElement* aElement,
|
||||
uint8_t aAttrEnum);
|
||||
|
|
|
@ -270,9 +270,9 @@ already_AddRefed<Path> SVGImageElement::BuildPath(PathBuilder* aBuilder) {
|
|||
// hit-testing against it. For that we just pretend to be a rectangle.
|
||||
|
||||
float x, y, width, height;
|
||||
MOZ_ASSERT(GetPrimaryFrame());
|
||||
SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height>(
|
||||
this, &x, &y, &width, &height);
|
||||
SVGGeometryProperty::ResolveAllAllowFallback<SVGT::X, SVGT::Y, SVGT::Width,
|
||||
SVGT::Height>(this, &x, &y,
|
||||
&width, &height);
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
return nullptr;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "DOMSVGPathSeg.h"
|
||||
#include "DOMSVGPathSegList.h"
|
||||
#include "SVGGeometryProperty.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "nsGkAtoms.h"
|
||||
|
@ -260,17 +261,17 @@ already_AddRefed<Path> SVGPathElement::BuildPath(PathBuilder* aBuilder) {
|
|||
uint8_t strokeLineCap = NS_STYLE_STROKE_LINECAP_BUTT;
|
||||
Float strokeWidth = 0;
|
||||
|
||||
if (auto* f = GetPrimaryFrame()) {
|
||||
const nsStyleSVG* style = f->StyleSVG();
|
||||
SVGGeometryProperty::DoForComputedStyle(this, [&](const ComputedStyle* s) {
|
||||
const nsStyleSVG* style = s->StyleSVG();
|
||||
// Note: the path that we return may be used for hit-testing, and SVG
|
||||
// exposes hit-testing of strokes that are not actually painted. For that
|
||||
// reason we do not check for eStyleSVGPaintType_None or check the stroke
|
||||
// opacity here.
|
||||
if (style->mStrokeLinecap != NS_STYLE_STROKE_LINECAP_BUTT) {
|
||||
strokeLineCap = style->mStrokeLinecap;
|
||||
strokeWidth = SVGContentUtils::GetStrokeWidth(this, f->Style(), nullptr);
|
||||
strokeWidth = SVGContentUtils::GetStrokeWidth(this, s, nullptr);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return mD.GetAnimValue().BuildPath(aBuilder, strokeLineCap, strokeWidth);
|
||||
}
|
||||
|
|
|
@ -170,10 +170,9 @@ bool SVGRectElement::GetGeometryBounds(Rect* aBounds,
|
|||
void SVGRectElement::GetAsSimplePath(SimplePath* aSimplePath) {
|
||||
float x, y, width, height, rx, ry;
|
||||
|
||||
MOZ_ASSERT(GetPrimaryFrame());
|
||||
SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height,
|
||||
SVGT::Rx, SVGT::Ry>(this, &x, &y, &width,
|
||||
&height, &rx, &ry);
|
||||
SVGGeometryProperty::ResolveAllAllowFallback<
|
||||
SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height, SVGT::Rx, SVGT::Ry>(
|
||||
this, &x, &y, &width, &height, &rx, &ry);
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
aSimplePath->Reset();
|
||||
|
@ -194,10 +193,9 @@ void SVGRectElement::GetAsSimplePath(SimplePath* aSimplePath) {
|
|||
already_AddRefed<Path> SVGRectElement::BuildPath(PathBuilder* aBuilder) {
|
||||
float x, y, width, height, rx, ry;
|
||||
|
||||
MOZ_ASSERT(GetPrimaryFrame());
|
||||
SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height,
|
||||
SVGT::Rx, SVGT::Ry>(this, &x, &y, &width,
|
||||
&height, &rx, &ry);
|
||||
SVGGeometryProperty::ResolveAllAllowFallback<
|
||||
SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height, SVGT::Rx, SVGT::Ry>(
|
||||
this, &x, &y, &width, &height, &rx, &ry);
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
return nullptr;
|
||||
|
|
|
@ -16,6 +16,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1474284
|
|||
<path id="path1" stroke="#000" fill="none"
|
||||
d="M 50,40
|
||||
C 50,40 0,60 30,20"/>
|
||||
<symbol font-size="10" width="20em" height="20em">
|
||||
<rect id="r1" x="5em" y="6em" width="20%" height="30%" />
|
||||
</symbol>
|
||||
</svg>
|
||||
|
||||
<pre id="test">
|
||||
|
@ -30,6 +33,10 @@ function expectValue(id, expected) {
|
|||
|
||||
function run() {
|
||||
expectValue("path1", 55.19);
|
||||
|
||||
let r1 = document.getElementById("r1");
|
||||
is(r1.getTotalLength(), 200, "getTotalLength() should work for display:none element");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче